【[六省聯考2017]組合數問題】
阿新 • • 發佈:2019-01-01
好水啊
但是我傻啊
我們設\(dp[i][j]=\sum_{t=0}^{∞}\binom{ik}{j+tk}\)
根據組合數萬年不變的遞推式\(\binom{n}{m}=\binom{n-1}{m-1}+\binom{n-1}{m}\)
我們有\(dp[i][j]=dp[i-1][j]+dp[i-1][(j-1+k)\%k]\)
顯然這個柿子可以用矩陣優化到\(log\)
於是就沒有了
有一個坑點就是當\(k=1\)的時候實際上是有\(dp[i][0]=2*dp[i-1][0]\),於是構造矩陣的時候小心一下就可以了
程式碼
#include<iostream> #include<cstring> #include<cstdio> #define re register #define LL long long inline int read() { re char c=getchar(); re int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } LL n,k,P,r; LL ans[51][51],a[51][51]; inline void did_a() { LL mid[51][51]; for(re int i=0;i<k;i++) for(re int j=0;j<k;j++) mid[i][j]=a[i][j],a[i][j]=0; for(re int i=0;i<k;i++) for(re int j=0;j<k;j++) for(re int p=0;p<k;p++) a[i][j]=(a[i][j]+(mid[i][p]*mid[p][j])%P)%P; } inline void did_ans() { LL mid[51][51]; for(re int i=0;i<k;i++) for(re int j=0;j<k;j++) mid[i][j]=ans[i][j],ans[i][j]=0; for(re int i=0;i<k;i++) for(re int j=0;j<k;j++) for(re int p=0;p<k;p++) ans[i][j]=(ans[i][j]+(mid[i][p]*a[p][j])%P)%P; } inline void out() { for(re int i=0;i<k;i++) { for(re int j=0;j<k;j++) printf("%d ",a[i][j]); putchar(10); } } inline void quick(LL b) { while(b) { if(b&1ll) did_ans(); b>>=1ll; did_a(); } } int main() { n=read(),P=read(),k=read(),r=read(); for(re int i=0;i<k;i++) ans[i][i]=1; for(re int i=1;i<k;i++) a[i][i]=1,a[i][i-1]=1; a[0][0]=1,a[0][k-1]+=1; quick(n*k); printf("%lld\n",ans[r][0]); return 0; }