http://www.lydsy.com/JudgeOnline/problem.php?id=4870

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

 

 80分暴力打的好爽 \(^o^)/~

 bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

预处理杨辉三角

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题 

令m=n*k

要求满足m&x==x ,x<=m, x%k==r 的x的个数

结论:若n&m==m,则C(n,m)为奇数,否则为偶数

枚举m的子集,判断是否%k==r

时间复杂度:O(m的位子集个数),即O(2^(m的二进制中1的个数))极限是O(n*k)

 

 bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

杨辉三角第i行的和=2^i,即bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

那么用2^(nk) 减去 前面不用的C

因为r<=50,所以这种C的个数<=50

暴力计算C即可

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

k=2 就是C(2n,r)+C(2n,r+2)+C(2n,r+4)……

是隔一个加一个

 杨辉三角 每一行的 奇数列之和=偶数列之和

所以 2^(2n)/2 - 前面不用的C,也是隔一个减一个

 

除2的计算 要乘2的逆元,但是p不是素数

 

所以 计算2^(2n) 改成计算 2^(2n-1)

 

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

预处理阶乘和阶乘的逆元,枚举计算C ,最多会计算n个C

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

不会,求指点

 

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

考虑组合数C(n,m)的实际意义:从n个元素里选出m个元素的方案数

那么本题就是求从n*k个元素里,选出%k=r个元素的方案数

dp[i][j] 表示从前i个元素里,选出%k=j 个元素的方案数

第i个不选:dp[i][j]+=dp[i-1][j]

第i个选:dp[i][j]+=dp[i-1][(j-1+k)%k]

矩阵乘法优化

bzoj千题计划263:bzoj4870: [六省联考2017]组合数问题

#include<cstdio>
#include<cstring>

using namespace std;

int p,K;

int a[50][50],ans[50][50],f[50][50];

int C[50][50];

void mul(int A[50][50],int B[50][50])
{
    memset(C,0,sizeof(C));
    for(int i=0;i<K;++i)
        for(int j=0;j<K;++j)
            for(int k=0;k<K;++k)
                C[i][j]=(C[i][j]+1LL*A[i][k]*B[k][j]%p)%p;
    for(int i=0;i<K;++i)
        for(int j=0;j<K;++j)
            A[i][j]=C[i][j];
}

int main()
{
    int n,k,r;
    scanf("%d%d%d%d",&n,&p,&K,&r);
    for(int i=0;i<=K-2;++i) a[i][i]=a[i][i+1]=1;
    a[K-1][K-1]++; a[K-1][0]++;
    f[0][0]=1;
    for(int i=0;i<K;++i) ans[i][i]=1;
    long long m=1LL*n*K;
    for(;m;mul(a,a),m>>=1)
        if(m&1) mul(ans,a);
    mul(f,ans);
    printf("%d",f[0][r]);
}
AC代码

相关文章: