Codeforces 1096G Lucky Tickets

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/85682446

题意:0~9种有k种可用数字,求前n/2个数字和与后n/2个数字和相等的方案数。 n < = 200000 n n<=200000 且 n为偶数

这题继续刷新我对多项式(常数)的认识。。。。。。
不难发现这个题就是多项式快速幂。。。。。。
然后我就想尝试一下新打的多项式 e x p exp 的版, O ( n log n ) O(n \log n) 是不是很资瓷啊。
然后极限数据连多项式求逆都要跑 5 s 5s 更不要说什么exp了。
正解居然是NTT一次然后对NTT后的数组每一个数 A A 变为 A k A^k
然后再NTT回来这种操作。
其实早就想过这种操作了,可是碍于网上的代码都不这么写,就没怎么想。
今天来一发我自己的理解。
上面的那个操作是正确的,多项式系列算法其实有两个mod,一个是题目给的998244353,还有一个就是答案多项式是在 m o d x n mod x^n 下计算的,不然项数可能会爆炸。
如果题目的答案需要mod任意一个mod,那么都不能NTT一次最后再NTT回来,因为中途你得NTT回来mod(如果mod的不是998244353,那么得需要拆系数FFT或3模数来保证不会炸long double / LL , 如果需要mod x^n , 那么得需要NTT回来把 x n x^n 以上的项全部清为0,不然NTT会变成循环卷积的,你的多项式求逆,求 ln \ln ,求 exp \exp 都是在这个意义下才成立的)。

所以如果是一个长度为 1 0 5 10^5 的多项式的 1 0 9 10^9 次幂 m o d x 1 0 5 mod x^{10^5} ,那么你NTT一次最后再NTT回来时你得开 2 1 0 14 2*10^{14} 的数组才能保证NTT不会变成循环卷积。。。。只能每次都NTT过去再NTT回来 m o d x n mod x^n ,或者多项式 exp \exp

而这个题是长度为10的多项式的 1 0 5 10^5 次幂,当然可以(小常数) n log n n \log n 的只NTT两次啦。

AC Code;

#include<bits/stdc++.h>
#define maxn (1<<21)+5
#define mod 998244353
using namespace std;

int r[maxn],inv[maxn]={1,1},w[maxn]={1},lg[maxn],wlen;
int Pow(int base,int k)
{ 	int ret = 1;
	for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll * ret* base % mod;
	return ret;}
void Init(int n)
{	for(wlen=1;n>=(wlen<<1);wlen<<=1);
	for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i] = 1ll * w[i-1] * pw  % mod;
	for(int i=2;i<=2*wlen;i++) 
		lg[i] = lg[i>>1] + 1, inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;}
inline void NTT(int A[maxn],int n,int tp)
{	int lgn = lg[n];
	for(int i=1;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(lgn-1));
	for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
	for(int L=2;L<=n;L<<=1)
		for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
			for(int k=st,x=0,tmp;k<st+l;k++,x+=inc)
				tmp=1ll*(tp==1?w[x]:w[2*wlen-x])*A[k+l]%mod,
				A[k+l]=(A[k]-tmp)%mod,A[k]=(A[k]+tmp)%mod;
	if(tp==-1) for(int i=0,inv=Pow(n,mod-2);i<n;i++) A[i]=1ll*A[i]*inv%mod;
}
void Inv(int *A,int *B,int n)
{	B[0]=Pow(A[0],mod-2),B[1]=0;
	static int tmp[maxn];
	for(int k=2;k<(n<<1);k<<=1)
	{	for(int i=0;i<(k<<1);i++) tmp[i]=i<k?A[i]:0,B[i]=i<k?B[i]:0;
		NTT(tmp,k<<1,1),NTT(B,k<<1,1);
		for(int i=0;i<(k<<1);i++) B[i]=1ll*B[i]*(2-1ll*B[i]*tmp[i]%mod)%mod;
		NTT(B,k<<1,-1);for(int i=min(n,k);i<(k<<1);i++) B[i]=0;}
}
void cLn(int *A,int *B,int n)
{	Inv(A,B,n);
	static int tmp[maxn];
	int lgn=lg[n-1]+2,len=1<<lgn;
	for(int i=0;i<len;i++) tmp[i]=i<n-1?1ll*A[i+1]*(i+1)%mod:0,B[i]=i<n?B[i]:0;
	NTT(tmp,len,1),NTT(B,len,1);
	for(int i=0;i<len;i++) tmp[i]=1ll*tmp[i]*B[i]%mod;
	NTT(tmp,len,-1);B[0]=0;
	for(int i=1;i<n;i++) B[i]=1ll*tmp[i-1]*inv[i]%mod;
	for(int i=n;i<len;i++) B[i] = 0;
}
void eXp(int *A,int *B,int n)
{	B[0]=1,B[1]=0;
	static int tmp[maxn];
	for(int k=2;k<(n<<1);k<<=1)
	{	cLn(B,tmp,k);
		for(int i=0;i<(k<<1);i++) tmp[i]=i<k?((i==0)-tmp[i]+A[i])%mod:0,B[i]=i<k?B[i]:0;
		NTT(tmp,k<<1,1),NTT(B,k<<1,1);
		for(int i=0;i<(k<<1);i++) B[i]=1ll*B[i]*tmp[i]%mod;
		NTT(B,k<<1,-1);for(int i=min(n,k);i<(k<<1);i++) B[i] = 0;
	}
}
void Pow(int *A,int *B,int n,int k)
{	int lgn = lg[n-1]+2 , len = 1 << lgn;
	for(int i=0;i<len;i++) B[i] = i<n?A[i]:0;
	NTT(B,len,1);
	for(int i=0;i<len;i++) B[i]=Pow(B[i],k);
	NTT(B,len,-1);
}
int a[maxn],b[maxn];
int main()
{
	int n,k,d[20]={};
	scanf("%d%d",&n,&k);
	for(int i=0;i<k;i++) scanf("%d",&d[i]);
	sort(d,d+k);
	for(int i=0;i<k;i++) a[d[i]-d[0]]++;
	int tp = d[k-1] - d[0];
	Init(n*10);
	Pow(a,b,n/2*tp+1,n/2);
	int ans = 0;
	for(int i=0;i<n*5;i++)
		ans = (ans + 1ll * b[i] * b[i] % mod) % mod;
	printf("%d\n",(ans+mod)%mod);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/85682446