Solution -「CCPC Winter Camp Day 6 A」Convolution

Description

Link.

给定一个数列 (sf a_1,a_2,....a_n),请求出下面这个结果在模 (sf 998244353) 下的答案。

[sum_{i=1}^{n}sum_{j=1}^{n}2^{a_i a_j} ]

Solution

这题涉及一个 trick:(sf ij=inom{i+j}{2}-inom{i}{2}-inom{j}{2}),是 UQ 里面 EI 给我讲的,说明一下。

首先我们令 (sf c_{a_{i}}=the~number~of~the~occurrences~of~a_{i},mx=max{a_{i}})

然后来推式子:

[egin{aligned} sfsum_{i=1}^{n}sum_{j=1}^{n}2^{a_{i}a_{j}}&=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}2^{ij} \ &=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}2^{inom{i+j}{2}-inom{i}{2}-inom{j}{2}} \ &=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}2^{inom{i+j}{2}}2^{-inom{i}{2}}2^{-inom{j}{2}} \ &=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}2^{inom{i+j}{2}}2^{-inom{i}{2}}2^{-inom{j}{2}} \ end{aligned}\ ]

现在我们令 (sf t_{i}=2^{inom{i}{2}},it_{i}=2^{-inom{i}{2}})

那么 (sf i+j) 那项你就直接拿出来最后单独算即可。

[egin{aligned} sfsum_{i=1}^{n}sum_{j=1}^{n}2^{a_{i}a_{j}} &=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}2^{inom{i+j}{2}}2^{-inom{i}{2}}2^{-inom{j}{2}} \ &=sfsum_{i=1}^{mx}sum_{j=1}^{mx}c_{i}c_{j}t_{i+j}it_{i}it_{j} \ end{aligned}\ ]

最后直接算:

[sf sum_{i=1}^{mx}sum_{j=0}^{mx-1}c_{i}it_{i}c_{i-j}it_{i-j} ]

卷积 完了。

记住模数非质的时候不一定有逆元啊啊啊啊!!!!

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=998244353;
void exGCD(LL one,LL ano,LL &x,LL &y)
{
	if(ano==0)	x=1,y=0;
	else	exGCD(ano,one%ano,y,x),y-=(one/ano)*x;
}
LL getInv(LL val,LL _MOD){LL res,w; exGCD(val,_MOD,res,w); return (res%_MOD+_MOD)%_MOD;}
LL fspow(LL bas,LL fur)
{
	LL res=1;
	while(fur)
	{
		if(fur&1)	res=LL(res)*bas%MOD;
		bas=LL(bas)*bas%MOD;
		fur>>=1;
	}
	return (res+MOD)%MOD;
}
LL fac[200010],ifac[200010];
//LL binom(LL n,LL k){return n>=k?LL(fac[n])*ifac[k]%(MOD-1)*ifac[n-k]%(MOD-1):0;}
LL binom(LL n,LL k){return n>=k?LL(n)*(n-1)/2%(MOD-1):0;}
namespace Poly
{
	typedef vector<LL> poly;
	#define len(x) (LL((x).size()))
	LL lim,rev[800010];
	void ntt(poly &f,LL op)
	{
		for(LL i=0;i<lim;++i)	if(i<rev[i])	swap(f[i],f[rev[i]]);
		for(LL len=2;len<=lim;len<<=1)
		{
			LL bas=fspow(op==1?3:332748118,(MOD-1)/len);
			for(LL fr=0;fr<lim;fr+=len)
			{
				LL now=1;
				for(LL ba=fr;ba<fr+(len>>1);++ba,now=LL(now)*bas%MOD)
				{
					LL tmp=LL(now)*f[ba+(len>>1)]%MOD;
					f[ba+(len>>1)]=(f[ba]-tmp+MOD)%MOD;
					f[ba]=(f[ba]+tmp)%MOD;
				}
			}
		}
		if(op==-1)
		{
			LL tmp=getInv(lim,MOD);
			for(LL i=0;i<lim;++i)	f[i]=LL(f[i])*tmp%MOD;
		}
	}
	poly operator*(poly f,poly g)
	{
		LL n=len(f)+len(g)-1; lim=1;
		while(lim<n)	lim<<=1;
		f.resize(lim),g.resize(lim);
		for(LL i=0;i<lim;++i)	rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
		ntt(f,1),ntt(g,1);
		for(LL i=0;i<lim;++i)	f[i]=LL(f[i])*g[i]%MOD;
		ntt(f,-1),f.resize(n);
		return f;
	}
}using namespace Poly;
LL n,mx,c[100010],t[100010],it[100010],ans,ex[200010];
int main()
{
	poly f;
	scanf("%lld",&n);
	for(LL i=1,x;i<=n;++i)	scanf("%lld",&x),mx=max(mx,x),++c[x];
	f.resize(mx+1);
	fac[0]=1;
	for(LL i=1;i<=(mx<<1);++i)	fac[i]=LL(fac[i-1])*i%(MOD-1);
//	for(LL i=0;i<=(mx<<1);++i)	ifac[i]=getInv(fac[i],MOD-1);
//	printf("%lld
",getInv(fac[2],MOD));
//	printf("(%lld %lld %lld %lld)
",fac[2],ifac[2],ifac[0],binom(2,2));
	for(LL i=0;i<=mx;++i)	t[i]=fspow(2,binom(i,2));
	for(LL i=0;i<=mx;++i)	it[i]=getInv(t[i],MOD);
//	for(LL i=1;i<=mx;++i)	printf("%lld ",fac[i]); puts("");
//	for(LL i=1;i<=mx;++i)	printf("%lld ",binom(i,2)); puts("");
//	for(LL i=1;i<=mx;++i)	printf("%lld ",LL(i)*(i-1)/2%MOD); puts("");
	for(LL i=0;i<=mx;++i)	f[i]=LL(c[i])*it[i]%MOD;
	for(LL i=1;i<=(mx<<1);++i)	ex[i]=fspow(2,binom(i,2));
	f=f*f;
	for(LL i=0;i<len(f);++i)	ans=(ans+LL(f[i])*ex[i]%MOD)%MOD;
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/orchid-any/p/14545662.html