题解 uoj498 新年的追逐战

Description

uoj498

Solution

首先考虑图的乘积有什么性质 .
考察 \(G_1\times G_2\), 不妨假设 \(G_1,G_2\) 连通 , 若不连通则可以拆成若干个连通块的乘积 .
如果有一个图为孤立点 , 那么联通块有 \(|G_1||G_2|\) 个 .
下面假设 \(G_1,G_2\) 都不为孤立点 .
发现 \((x_1,y_1)\)\((x_2,y_2)\) 连通当且仅当存在两条长度相同的路径在 \(G_1\) 中从 \(x_1\)\(x_2\), 在 \(G_2\) 中从 \(y_1\)\(y_2\).
发现可以在一条边上反复横跳 , 那么实际上只关心路径的奇偶性 .
可以按照原图是不是二分图来讨论 , 那么显然有:

  1. 两图都不是二分图 , 会形成一个非二分图连通块 .
  2. 两图恰好有一个二分图 , 会形成一个二分图连通块 .
  3. 两图都为二分图 , 会形成两个二分图连通块 .

计算孤立点对答案的贡献

若最后每张图中非孤立点的数量为 \(a_i\)
那么这种方案下孤立点对答案的贡献为 \(\prod m_i-\prod a_i\)
只需计算 \(\prod a_i\) 的期望 , 易得 \(\displaystyle E(a_i)=m_i(1-(\frac12)^{m_i-1})\)

计算其他点对答案的贡献

考虑没有孤立点的图的乘积 .
\(G_0,G_1\) 中连通块 , 二分图连通块个数分别为 \(A_0,A_1,B_0,B_1\)
那么由上面的结论可知 .
乘积 \(G_2=G_0\times G_1\) 中连通块 , 二分图连通块个数分别为 \(A_0B_0+A_1B_1,A_1B_0+A_0B_1\)
发现是异或卷积的形式 , 那么可以先 FWT, 然后把对应点值都乘起来 , 最后 IFWT 即可 .
那么现在问题是求出 \(n\) 个点的图的连通块个数在全部情况下的和以及\(n\) 个点的图的二分图连通块个数在全部情况下的和.

计算连通块个数

\(n\) 个点的图的个数的 EGF 为 \(F(x)\),\(n\) 个点的连通图的个数的 EGF 为 \(G(x)\)
显然有 \(\displaystyle F(x)=\sum\limits_i\frac{2^{\binom i2}}{i!}x^i\).
考虑到图可以被拆解为若干连通图 , 那么有 \(F(x)=e^{G(x)},G(x)=\ln F(x)\)
\(n\) 个点的图的连通块个数的 EGF 为 \(H(x)\)
那么有 \(H(n)=\sum\limits_i F(n-i)G(i),H(x)=F(x)G(x)\), 含义为统计大小为 \(i\) 的连通块对答案的贡献 .

计算二分图连通块个数

\(n\) 个点的二分染色图的个数的 EGF 为 \(F(x)\),\(n\) 个点的二分图的个数的 EGF 为 \(G(x)\).
二分染色图定义为给每个点染色 , 同色点之间不能连边 .
那么有 \(F(x)=G^2(x)\), 考虑每个二分连通块内的编号最小点被染成了什么色分成两类即可证明 .

\(\begin{aligned}F(x)&=\sum\limits_ix^i\sum\limits_{j=0}^i\frac{2^{(i-j)j}}{j!(i-j)!}\\&=\sum\limits_ix^i2^{\binom i2}\sum\limits_{j=0}^i \frac{2^{-\binom j2}}{j!}\frac{2^{-\binom {i-j}2}}{(i-j)!} \end{aligned}\)
那么就可求出 \(F(x)\) .
\(H(x)\)\(n\) 个点的连通二分图的个数的 EGF .
那么依然有 \(\displaystyle G(x)=e^{H(x)},H(x)=\ln G(x)=\frac12\ln F(x)\)
\(n\) 个点的图的个数的 EGF 为 \(A(x)\)
那么和上面一样 , 答案为 \(A(x)H(x)\)

记得在求以上两项时要减去孤立点的贡献 .

时间复杂度 \(O(n\log n)\)

Code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int read()
{
	int ret=0;bool f=0;char c=getchar();
	while(c>'9'||c<'0')f|=(c=='-'),c=getchar();
	while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
	return f?-ret:ret;
}
const int mod=998244353;
const int maxn=1e5+5;
int n,a[maxn],mx,pow2[maxn],fac[maxn],ifac[maxn];
int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=(ll)a*a%mod)if(b&1)ret=(ll)ret*a%mod;return ret;}
int R[1<<21],W[1<<21],inv[maxn];
void prework()
{
	inv[1]=1;for(int i=2;i<=mx;i++)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
	for(int i=0;i<=mx;i++)pow2[i]=qpow(2,((ll)i*(i-1)/2)%(mod-1));
	fac[0]=1;for(int i=1;i<=mx;i++)fac[i]=(ll)fac[i-1]*i%mod;
	ifac[0]=1;for(int i=1;i<=mx;i++)ifac[i]=(ll)ifac[i-1]*inv[i]%mod;
}
struct poly
{
	vector<int>v;
	int&operator[](const int &i){return v[i];}
	void set(int l){v.resize(l);}
	int len(){return v.size();}
	void ntt(int L,int typ)
	{
		int n=1<<L;
		for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
		W[0]=1;W[1]=qpow(3,(mod-1)/n);if(typ==-1)W[1]=qpow(W[1],mod-2);
		for(int i=2;i<n;i++)W[i]=(ll)W[i-1]*W[1]%mod;
		set(n);
		for(int i=0;i<n;i++)if(R[i]>i)swap(v[R[i]],v[i]);
		for(int t=n>>1,d=1;d<n;d<<=1,t>>=1)
			for(int i=0;i<n;i+=(d<<1))
				for(int j=0;j<d;j++)
				{
					int tmp=(ll)W[t*j]*v[i+j+d]%mod;
					v[i+j+d]=(v[i+j]-tmp+mod)%mod;
					v[i+j]=(v[i+j]+tmp)%mod;
				}
		if(typ==-1){int inv=qpow(n,mod-2);for(int i=0;i<n;i++)v[i]=(ll)v[i]*inv%mod;}
	}
	void adjust(){while(len()>1&&v.back()==0)v.pop_back();}
	poly operator *(poly &x)
	{
		poly ret,tmp0=*this,tmp1=x;
		int L=ceil(log2(len()+x.len())),n=1<<L;
		ntt(L,1);x.ntt(L,1);
		ret.set(n);
		for(int i=0;i<n;i++)ret[i]=(ll)v[i]*x[i]%mod;
		ret.ntt(L,-1);ret.adjust();*this=tmp0;x=tmp1;return ret;
	}
	void operator *=(poly &x)
	{
		poly tmp=x;
		int L=ceil(log2(len()+x.len())),n=1<<L;
		ntt(L,1);x.ntt(L,1);
		for(int i=0;i<n;i++)v[i]=(ll)v[i]*x[i]%mod;
		ntt(L,-1);x=tmp;adjust();
	}
	void squ()
	{
		int L=ceil(log2(len()))+1,n=1<<L;
		ntt(L,1);
		for(int i=0;i<n;i++)v[i]=(ll)v[i]*v[i]%mod;
		ntt(L,-1);adjust();
	}
	poly getinv(int deg=-1)
	{
		if(deg==-1)deg=len();
		if(deg==1)return {{qpow(v[0],mod-2)}};
		poly ret=getinv((deg+1)>>1);
		int L=ceil(log2(deg))+1,n=1<<L;
		poly tmp;tmp.set(deg);
		for(int i=0;i<deg;i++)tmp[i]=v[i];
		tmp.ntt(L,1);ret.ntt(L,1);
		for(int i=0;i<n;i++)ret[i]=(ll)ret[i]*(2-(ll)tmp[i]*ret[i]%mod+mod)%mod;
		ret.ntt(L,-1);ret.set(deg);return ret;
	}
	poly getln(int deg=-1)
	{
		if(deg==-1)deg=len();
		poly ret,rev=getinv();
		ret.set(deg);
		for(int i=0;i+1<len();i++)ret[i]=(ll)v[i+1]*(i+1)%mod;
		ret*=rev;
		ret.set(deg);
		for(int i=deg-1;i>=1;i--)ret[i]=(ll)ret[i-1]*inv[i]%mod;
		ret[0]=0;
		return ret;
	}
};
int solve_independent_node()
{
	int mul1=1,mul2=1;
	for(int i=1;i<=n;i++)mul1=(ll)mul1*a[i]%mod;
	for(int i=1;i<=n;i++)mul2=(ll)mul2*a[i]%mod*(1-qpow(inv[2],a[i]-1)+mod)%mod;
	mul1=(mul1-mul2+mod)%mod;
	for(int i=1;i<=n;i++)mul1=(ll)mul1*pow2[a[i]]%mod;
	return mul1;
}
poly get_connnect_blocks()
{
	poly graphnum;
	graphnum.set(mx+1);
	for(int i=0;i<=mx;i++)graphnum[i]=(ll)pow2[i]*ifac[i]%mod;
	poly ln=graphnum.getln();
	graphnum*=ln;
	graphnum.set(mx+1);
	return graphnum;
}
poly get_bipartie_connect_blocks()
{
	poly graphnum;
	graphnum.set(mx+1);
	for(int i=0;i<=mx;i++)graphnum[i]=(ll)pow2[i]*ifac[i]%mod;
	poly f;
	f.set(mx+1);
	for(int i=0;i<=mx;i++)f[i]=(ll)ifac[i]*qpow(pow2[i],mod-2)%mod;
	f.squ();f.set(mx+1);
	for(int i=0;i<=mx;i++)f[i]=(ll)f[i]*pow2[i]%mod;
	f=f.getln();
	for(int i=0;i<=mx;i++)f[i]=(ll)f[i]*inv[2]%mod;
	graphnum*=f;
	graphnum.set(mx+1);
	return graphnum;
}
int solve_connected_block()
{
	poly f=get_connnect_blocks(),g=get_bipartie_connect_blocks();
	for(int i=1;i<=mx;i++)// substract independent nodes
	{
		f[i]=((ll)f[i]*fac[i]%mod-(ll)i*pow2[i-1]%mod+mod)%mod;
		g[i]=((ll)g[i]*fac[i]%mod-(ll)i*pow2[i-1]%mod+mod)%mod;
	}
	int mul1=1,mul2=1;
	for(int i=1;i<=n;i++)
	{
		mul1=(ll)mul1*(f[a[i]]+g[a[i]])%mod;
		mul2=(ll)mul2*(f[a[i]]-g[a[i]]+mod)%mod;
	}
	return (ll)(mul1+mul2)*inv[2]%mod;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)mx=max(mx,a[i]=read());
	prework();
	printf("%d\n",(solve_independent_node()+solve_connected_block())%mod);
	return 0;
}
posted @ 2022-01-04 21:32  zero4338  阅读(72)  评论(0编辑  收藏  举报