[六省联考2017] 组合数问题

一、题目

点此看题

二、解法

其实不一定是递推问题才想矩阵加速,比如某个东西很大,但是某个东西很小的时候就可以尝试矩阵乘法了。

这道题就用很小的量来定义状态就行了,设 \(f(i,j)\) 表示考虑了 \(i\) 个数,选的总数模 \(k\)\(j\),那么每次就考虑这个数选还是不选,不难写出转移(第二维在模意义下):

\[f(i,j)=f(i-1,j)+f(i-1,j-1) \]

这个就很容易矩阵乘法了,时间复杂度 \(O(k^3\log(nk))\),注意考虑下 \(k=1\) 的情况。


还有一个做法也是利用了快速幂去算这东西,只不过用的是多项式的快速幂。那么如果我们要去套多项式快速幂怎么办呢?这就要求我们找到沟通组合数和多项式的桥梁了,这里可以用二项式定理:

\[\begin{aligned} &=\sum_{i\bmod k=r}{nk\choose i}\\ &=\sum_{i\bmod k=r}[x^i](1+x)^{nk}\\ &=[x^r]((1+x)^{nk}\bmod (x^k-1)) \end{aligned} \]

直接循环卷积快速幂,时间复杂度 \(O(k^2\log (nk))\)不会真的有人无聊到去写多项式乘法吧

#include <cstdio>
const int M = 1005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,p,k,r,a[M],b[M],A[M],B[M];
void mul(int *a,int *b)
{
	for(int i=0;i<k;i++) A[i]=a[i],B[i]=b[i],b[i]=0;
	for(int i=0;i<k;i++)
		for(int j=0;j<k;j++)
			b[(i+j)%k]=(b[(i+j)%k]+A[i]*B[j])%p;
}
signed main()
{
	n=read();p=read();k=read();r=read();
	if(k==1) a[0]=2%p;//这时候都在常数项
	else a[0]=a[1]=1;
	n*=k;b[0]=1;
	while(n>0)
	{
		if(n&1) mul(a,b);
		mul(a,a);
		n>>=1;
	}
	printf("%lld\n",b[r]);
}
posted @ 2021-03-30 21:59  C202044zxy  阅读(65)  评论(0编辑  收藏  举报