http://www.lydsy.com/JudgeOnline/problem.php?id=4870
80分暴力打的好爽 \(^o^)/~
预处理杨辉三角
令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)
杨辉三角第i行的和=2^i,即
那么用2^(nk) 减去 前面不用的C
因为r<=50,所以这种C的个数<=50
暴力计算C即可
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)
预处理阶乘和阶乘的逆元,枚举计算C ,最多会计算n个C
不会,求指点
考虑组合数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]
矩阵乘法优化
#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]); }