1. 程式人生 > 其它 >六省聯考2017題解

六省聯考2017題解

[六省聯考 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;
}