[Shoi2017]组合数问题 BZOJ4870
这道题可以根据组合数的实际意义来理解,就是从n*k个物品中选择除k余r个物品的方案数,那么就可以得到用f[i][j]表示在前i个物品中,选择j个物品的方案数,其中j是对k取模后的结果,那么f[i][j]=f[i-1][j](在第i为不取)+f[i-1][(j-1+k)%k](在第i为取),可以发现,第i位只与i-1位有关系那么久可以用矩阵快速幂优化,
在做矩阵乘法的时候,要特别注意矩阵乘特别容易爆int,所以矩阵数组要开成long long
另外这道题还有一个坑点,就是当k=1,r=0是,转移到f[i][0]的两部分是相同的,所以在初始矩阵的时候不能简单的把a[i][j]赋值成1,而应该把a[i][j]++;
1 #include<cmath> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 int n,p,K,R; 9 int f[60]; 10 int a[60][60]; 11 long long c[60]; 12 void cheng1(){ 13 memset(c,0,sizeof(c)); 14 for(int h=0;h<K;h++){ 15 for(int i=0;i<K;i++){ 16 c[h]+=(long long )f[i]*a[i][h]; 17 c[h]%=p; 18 } 19 } 20 for(int i=0;i<K;i++) 21 f[i]=c[i]; 22 } 23 long long d[60][60]; 24 void cheng2(){ 25 memset(d,0,sizeof(d)); 26 for(int h=0;h<K;h++){ 27 for(int i=0;i<K;i++){ 28 for(int j=0;j<K;j++){ 29 d[i][j]+=(long long)a[i][h]*a[h][j]; 30 d[i][j]%=p; 31 } 32 } 33 } 34 for(int i=0;i<K;i++){ 35 for(int j=0;j<K;j++){ 36 a[i][j]=d[i][j]; 37 } 38 } 39 } 40 int main(){ 41 freopen("a.in","r",stdin); 42 freopen("3.out","w",stdout); 43 scanf("%d%d%d%d",&n,&p,&K,&R); 44 f[0]=1; 45 for(int i=0;i<K;i++){ 46 a[i][i]++; 47 if(i==0){ 48 a[K-1][i]++; 49 continue; 50 } 51 a[(i-1)][i]++; 52 } 53 long long t=(long long)n*K; 54 while(t){ 55 if(t&1){ 56 cheng1(); 57 } 58 cheng2(); 59 t=t>>1; 60 // cout<<"t= "<<t<<endl; 61 } 62 cout<<f[R]<<endl; 63 return 0; 64 65 }