Biggest Set Ever

XXXIV.Biggest Set Ever

可能看不到题,简洁给一下题意:

\(\prod\limits_{i=0}^{T-1}(1+x^i)\) 中所有次数 \(\equiv m\pmod{n}\) 的项的系数之和。

数据范围:\(0\leq m<n\leq10000,1\leq T<10^{100000}\)

首先,可以发现,因为次数对 \(n\) 取模(相当于循环卷积),故实际上对于 \(i<T\bmod n\) ,出现了 \(\left\lceil\dfrac{T}{n}\right\rceil\)\((1+x^i)\) 的项;对于剩余的部分,出现了 \(\left\lfloor\dfrac{T}{n}\right\rfloor\)\((1+x^i)\) 的项;

于是我们令 \(p=T\bmod n,q=\left\lfloor\dfrac{T}{n}\right\rfloor\)。则,原式等价于\(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^q\prod\limits_{i=0}^{p-1}(1+x^i)\)

两边括号里的东西都可以 \(O(n^2)\) 地暴力乘出来(这里吐槽一下数据范围,\(n^2\) 跑一万会让人觉得比较不寻常)。然后,左边括号里的东西上一个多项式快速幂即可。

需要注意的是,常规卷积意义下的多项式快速幂的模数(设为 \(P\)),在指数上的模数也是 \(P\) 本身;然而,循环卷积意义下的模数,是有不同的表现的。

考虑 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^P\)。考虑其中某一项 \((1+x^i)^P\)。考虑二项式展开得到 \(\sum\limits_{j=0}^P\dbinom{P}{j}x^{ij}\)。因为所有系数在 \(P\) 意义下取模,所以只需保留 \(\dbinom{P}{0}\)\(\dbinom{P}{P}\),其它东西在模意义下都是 \(0\)。故有 \((1+x^i)^P=1+x^{iP}\)。因为 \(P\)\(n\) 互质,所以 \(\prod\limits_{i=0}^{n-1}(1+x^{iP})\),就等价于 \(\prod\limits_{i=0}^{n-1}(1+x^i)\)——\(P\)\(0\sim n-1\) 倍刚好取到 \(0\sim n-1\) 中所有元素(扩展欧拉定理)。

于是我们只需要关于 \(P-1\) 取模就行了。需要注意的是,当被模的数非零且模完后恰为 \(0\) 时,其应该被当作 \(P-1\) 看待——因为,我们只证明了 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^P=\prod\limits_{i=0}^{n-1}(1+x^i)\),而并没有证明 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^{P-1}=1\)——事实上,二者在大多数时候都是不等的!

代码(最后的注释是样例):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int G=3;
int ksm(int x,int y){
	int rt=1;
	for(;y;x=(1ll*x*x)%mod,y>>=1)if(y&1)rt=(1ll*rt*x)%mod;
	return rt;
}
int lim=1,LG,invlim,rev[40100];
void NTT(int *a,int tp){
	for(int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int md=1;md<lim;md<<=1){
		int rt=ksm(G,(mod-1)/(md<<1));
		if(tp==-1)rt=ksm(rt,mod-2);
		for(int stp=md<<1,pos=0;pos<lim;pos+=stp){
			int w=1;
			for(int i=0;i<md;i++,w=(1ll*w*rt)%mod){
				int x=a[pos+i],y=(1ll*w*a[pos+md+i])%mod;
				a[pos+i]=(x+y)%mod;
				a[pos+md+i]=(x-y+mod)%mod;
			}
		}
	}
	if(tp==-1)for(int i=0;i<lim;i++)a[i]=(1ll*a[i]*invlim)%mod;
}
int n,m,p,q,f[10010],g[10010],h[10010];
int A[40100],B[40100];
void mul(int *a,int *b,int *c){
	for(int i=0;i<n;i++)A[i]=a[i],B[i]=b[i],c[i]=0;
	NTT(A,1),NTT(B,1);
	for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
	NTT(A,-1);
	for(int i=0;i<lim;i++)(c[i%n]+=A[i])%=mod,A[i]=B[i]=0;
}
void read(){
	char c=getchar();
	bool over=false;
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9'){
		p=(p<<3)+(p<<1)+(c^48),q=(10*q+(p/n))%(mod-1);
		if(p>=n)over=true;
		p%=n,c=getchar();
	}
	if(over&&!q)q=mod-1;
}
void ksm(int *a,int *b,ll y){
	for(int i=0;i<n;i++)b[i]=0;b[0]=1;
	for(;y;y>>=1,mul(a,a,a))if(y&1)mul(a,b,b);
}
int main(){
	scanf("%d%d",&n,&m);
	read();
	while(lim<=(n<<1))lim<<=1,LG++;invlim=ksm(lim,mod-2);
	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(LG-1));
	f[0]=1;
	for(int i=0;i<p;i++){
		for(int j=0;j<n;j++)h[j]=f[j];
		for(int j=0;j<n;j++)(h[(j+i)%n]+=f[j])%=mod;
		for(int j=0;j<n;j++)f[j]=h[j];
	}
	g[0]=1;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)h[j]=g[j];
		for(int j=0;j<n;j++)(h[(j+i)%n]+=g[j])%=mod;
		for(int j=0;j<n;j++)g[j]=h[j];
	}
	ksm(g,h,q);
	mul(h,f,g);
	printf("%d\n",g[m]);
	return 0;
}
/*
3 2 5
1 0 20
*/

原文地址:https://www.cnblogs.com/Troverld/p/14610720.html