【[六省联考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;
}
posted @ 2019-01-01 21:44  asuldb  阅读(131)  评论(0编辑  收藏  举报