六省聯考2017題解
阿新 • • 發佈:2022-03-08
[六省聯考 2017]組合數問題
觀察資料範圍,發現\(n\)非常大,但是\(k\)和\(r\)很小,容易想到矩陣乘法。
原題式子的組合意義就是從\(n \times k\)個物品選擇\(i(i \bmod k=r)\)個物品。
考慮dp,設\(f_{i,j}\)表示從\(i\)個物品中選擇\(s(s \bmod k=j)\)個物品。
由於\(C_{i,j}=C_{i-1,j-1}+C_{i-1,j}\),因此可以推出\(f_{i,j}=f_{i-1,j}+f_{i-1,(j-1) \bmod k}\)。
發現所有的\(f_{i,j}\)都是從\(i-1\)轉移過來的,因此可以矩陣乘法優化。
複雜度:\(O(k^3 \log n)\)
#include<bits/stdc++.h> using namespace std; const int maxn=120; int n,k,r,mod; struct Matrix { int n,m; int c[maxn][maxn]; Matrix() { n=0,m=0; for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) c[i][j]=0; } Matrix operator * (Matrix B)const { Matrix C;C.n=n,C.m=B.m; for(int i=1;i<=n;i++) for(int j=1;j<=B.m;j++) for(int k=1;k<=m;k++) C.c[i][j]=(C.c[i][j]+1ll*c[i][k]*B.c[k][j]%mod)%mod; return C; } Matrix power(long long k) { Matrix C,ans;C.n=ans.n=n,C.m=ans.m=m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) C.c[i][j]=c[i][j]; for(int i=1;i<=n;i++) ans.c[i][i]=1; while(k) { if(k&1ll) ans=ans*C; C=C*C; k>>=1ll; } return ans; } }Ans,F; int main() { scanf("%d%d%d%d",&n,&mod,&k,&r); F.n=k,F.m=1; F.c[1][1]=1; Ans.n=Ans.m=k; for(int i=1;i<=k;i++) { int now=i-1; if(now==0) now=k; Ans.c[i][i]+=1;Ans.c[i][now]+=1; } Ans=Ans.power(1ll*n*k); F=Ans*F; printf("%d\n",F.c[r+1][1]); return 0; }