UOJ498
大量生成函数!
大概是给出 \(n\) 个无向图大小,连边概率 \(\frac{1}{2}\) ,定义 \(G_1\times G_2=(V',E')\) 为图的乘积,然后最后求
\[V=\{(a_1,a_2...a_n)|a_1\in V_1,a_2\in V_2...a_n\in V_n\}
\]
\[E=\{((a_1,a_2...a_n),(b_1,b_2...b_n))|(a_1,b_1)\in E_1,(a_2,b_2)\in E_2...(a_n,b_n)\in E_n\}
\]
这样的图的联通块的个数。
把边集转化为现在每个联通块上有一个棋子在\(a_i\),每次可以把每个棋子向别的方向动一步,每次动的始末就构成了一条边。
首先如果点集里面有孤立点那么显然它的边集为空,下面考虑非孤立点。
那我们又发现这个乘积具有交换律和结合律,考虑一个一个乘起来,先考虑图联通的情况。
- 两个二分图乘积是两个二分图。
考虑将两个二分图黑白染色,则若一开始\(a_i\)同色则会一直同色,否则会一直异色,并且这样的图上不存在奇环,那么同色和异色就形成了两个新的二分图。 - 一个二分图和一个非二分连通图乘积是一个二分图。
考虑将二分图黑白染色,我们在非二分图上奇环上转一圈,二分图上就变成了异色,又因为二分图上没有奇环,要回到一个点需要走偶数次,所以新图也是一个二分图。 - 两个非二分连通图的乘积是一个非二分联通图。
这个存在奇环,并且可以一个反复横跳一个在奇环上乱转,于是可以联通。
那我们现在考虑完联通情况,考虑整个图,可以写成\([n,a,b,c]\)表示点数,孤立点数,联通二分图数,联通非二分图数考虑。
由上面的情况,有了\([n,a,b,c]\times[m,d,e,f]\to[nm,am+dn-ad,2be+bf+ce,cf]\)。
那么我们求出每个图的这四个参最后乘起来,后三个参之和就是答案了。
然后进入生成函数环节:
\(G_1\)为有标号无向图方案数的\(EGF\)
\[G_1=\sum\limits_{i=0}^{inf}\frac{2^{\binom{i}{2}}x^i}{i!}
\]
\(G_2\)为有标号无向连通图方案数的\(EGF\),根据指数公式定理,有:
\[G_2=\ln G_1
\]
\(G_3\)为n个点的有标号无向图中联通块数总和的\(EGF\)
\[G_3=G_1G_2
\]
\(G_4\)为n个点的有标号无向图中孤立点数总和的\(EGF\)
\[G_4=xG_1
\]
\(G_5\)为n个点二染色有标号无向图方案数的\(EGF\)
\[G_5=\sum\limits_{i=0}^{inf}\sum\limits_{j=0}^{inf}\binom{i+j}{i}2^{ij}\frac{x^{i+j}}{(i+j)!}
\]
\[=\sum\limits_{i=0}^{inf}\sum\limits_{j=0}^{inf}\frac{2^{-\binom{i}{2}}}{i!}\frac{2^{-\binom{j}{2}}}{j!}2^{\binom{i+j}{2}}x^{i+j}
\]
\[=\sum\limits_{n=0}^{inf}2^{\binom{n}{2}}x^n[x^n](\sum\limits_{i=0}^{inf}\frac{2^{-\binom{i}{2}}x^i}{i!})^2
\]
\(G_6\)为n个点非孤立点有标号联通无向二分图的\(EGF\),里面除去了二染色方案数和孤立点
\[G_6=\frac{\ln G_5}{2}-x
\]
\(G_7\)为n个点非孤立点有标号无向二分图联通块个数总和的\(EGF\)
\[G_7=G_1G_5
\]
\(G_8\)为n个点非二分图联通块总和
\[G_8=G_3-G_4-G_7
\]
然后乘起来就完事了
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
typedef long long ll;typedef double db;
#define pf printf
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
const int N=1e6+10,mod=998244353,inv2=(mod+1)>>1;
inline int M(int x){return x>=mod?x-mod:x;}inline void add(int &x,int v){x+=v,x-=x>=mod?mod:0;}
inline int ksm(int a,int b){int ans=1;while(b){if(b&1)ans=1ll*a*ans%mod;a=1ll*a*a%mod;b>>=1;}return ans;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
namespace poly{
int r[N],G[N],Gi[N];
inline void init(int lim,int l){
for(int i=0;i<lim;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
G[0]=1,G[1]=ksm(3,(mod-1)/lim);
for(int i=2;i<lim;i++)G[i]=1ll*G[i-1]*G[1]%mod;
Gi[0]=1,Gi[1]=ksm(G[1],mod-2);
for(int i=2;i<lim;i++)Gi[i]=1ll*Gi[i-1]*Gi[1]%mod;
}
inline void ntt(int *a,int tp,int lim){
for(int i=0;i<lim;i++)if(i<r[i])swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1){
for(int j=0;j<lim;j+=(mid<<1)){
for(int k=0;k<mid;k++){
int w=tp==1?G[lim/(mid<<1)*k]:Gi[lim/(mid<<1)*k];
int x=a[j+k],y=1ll*w*a[j+mid+k]%mod;
a[j+k]=M(x+y),
a[j+mid+k]=M(x-y+mod);
}
}
}
if(tp==-1){
int inv=ksm(lim,mod-2);
for(int i=0;i<lim;i++)a[i]=1ll*a[i]*inv%mod;
}
}
inline void getinv(int *a,int *b,int n){
static int A[N],B[N];
b[0]=ksm(a[0],mod-2);int len,lim,l;
for(len=1;len<(n<<1);len<<=1){
lim=1,l=0;
while(lim<=len)lim<<=1,l++;
init(lim,l);
for(int i=0;i<len;i++)A[i]=a[i],B[i]=b[i];
ntt(A,1,lim),ntt(B,1,lim);
for(int i=0;i<lim;i++)b[i]=1ll*(2ll-1ll*B[i]*A[i]%mod+mod)*B[i]%mod;
ntt(b,-1,lim);
for(int i=len;i<lim;i++)b[i]=0;
}
memset(A,0,sizeof(int)*(len));
memset(B,0,sizeof(int)*(len));
memset(b+n,0,sizeof(int)*(len-n));
}
int inv[N];
inline void init(int n){inv[0]=inv[1]=1;F(i,2,n)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;}//getln 时记得提前调用init
inline void getdao(int *a,int *b,int n){for(int i=1;i<n;i++)b[i-1]=1ll*i*a[i]%mod;b[n-1]=0;}
inline void jif(int *a,int *b,int n){for(int i=1;i<n;i++)b[i]=1ll*inv[i]*a[i-1]%mod;b[0]=0;}
inline void getln(int *a,int *b,int n){
static int A[N],B[N];
getdao(a,A,n),getinv(a,B,n);
int lim=1,l=0;
while(lim<=(n<<1))lim<<=1,l++;
init(lim,l);
ntt(A,1,lim),ntt(B,1,lim);
for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
ntt(A,-1,lim);jif(A,b,n);
memset(A,0,sizeof(int)*(lim));
memset(B,0,sizeof(int)*(lim));
}
inline void mul(int *a,int *b,int *c,int n,int m,int t){
static int A[N],B[N];int lim=1,l=0;
memcpy(A,a,sizeof(int)*(n+1));
memcpy(B,b,sizeof(int)*(m+1));
while(lim<=(n+m))lim<<=1,l++;init(lim,l);
ntt(A,1,lim),ntt(B,1,lim);
for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
ntt(A,-1,lim);F(i,0,t)c[i]=A[i];
memset(A,0,sizeof(int)*lim),
memset(B,0,sizeof(int)*lim);
}
}
inline ll C2(int x){return 1ll*x*(x-1)/2;}
int jc[N],inv[N],n,a[N];
int G[9][N],lim,v[4][N];
inline short main(){
file();
n=read();
F(i,1,n)a[i]=read(),lim=max(lim,a[i]+5);
jc[0]=inv[0]=inv[1]=1;F(i,1,lim)jc[i]=1ll*jc[i-1]*i%mod;
inv[lim]=ksm(jc[lim],mod-2);D(i,lim-1,2)inv[i]=1ll*inv[i+1]*(i+1)%mod;
F(i,0,lim)G[1][i]=1ll*ksm(2,C2(i)%(mod-1))*inv[i]%mod;
poly::init(lim<<1);poly::getln(G[1],G[2],lim);
poly::mul(G[1],G[2],G[3],lim,lim,lim);
F(i,1,lim)G[4][i]=G[1][i-1];
F(i,0,lim)G[0][i]=1ll*ksm(inv2,C2(i)%(mod-1))*inv[i]%mod;
poly::mul(G[0],G[0],G[5],lim,lim,lim);
F(i,0,lim)G[5][i]=1ll*G[5][i]*ksm(2,C2(i)%(mod-1))%mod;
poly::getln(G[5],G[6],lim);
F(i,0,lim)G[6][i]=1ll*G[6][i]*inv2%mod;add(G[6][1],mod-1);
poly::mul(G[1],G[6],G[7],lim,lim,lim);
F(i,0,lim)G[8][i]=M(M(G[3][i]+mod-G[4][i])+mod-G[7][i]);
F(i,0,lim)v[0][i]=1ll*i*ksm(2,C2(i)%(mod-1))%mod,
v[1][i]=1ll*G[4][i]*jc[i]%mod,
v[2][i]=1ll*G[7][i]*jc[i]%mod,
v[3][i]=1ll*G[8][i]*jc[i]%mod;
int ans[4]={1,0,0,1};
F(i,1,n){
const int v0=ans[0],v1=ans[1],v2=ans[2],v3=ans[3];
ans[0]=1ll*v0*v[0][a[i]]%mod;
ans[1]=M(M(1ll*v1*v[0][a[i]]%mod+1ll*v0*v[1][a[i]]%mod)+mod-1ll*v1*v[1][a[i]]%mod);
ans[2]=M(M(2ll*v2*v[2][a[i]]%mod+1ll*v2*v[3][a[i]]%mod)+1ll*v3*v[2][a[i]]%mod);
ans[3]=1ll*v3*v[3][a[i]]%mod;
}pi(M(M(ans[1]+ans[2])+ans[3]));
return 0;
}
}
signed main(){return EMT::main();}
Everything that kills me makes me feel alive.