【[六省联考2017]组合数问题】
好水啊
但是我傻啊
我们设\(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;
}