1. 程式人生 > >[Shoi2017]組合數問題 BZOJ4870

[Shoi2017]組合數問題 BZOJ4870

根據 zoj pen 理解 mem math clu 不能 using

這道題可以根據組合數的實際意義來理解,就是從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 }

[Shoi2017]組合數問題 BZOJ4870