YJC plays Minecraft
YJC is an old driver of mini train. Today when he was playing his favorite game Minecraft, he found (n) islands. YJC numbered them (1)...(n), and on the (i)th island, there are ({a}_{i}) cities. Every two cities on the same island are connected directly by a road. Also, the ({a}_{i})th city on the (i)th island and the (1)th city on the (i+1)th island are connected by an underwater railway. ((1leq i< n)) Specially, the ({a}_{n})th city on the (n)th island and the (1)th city on the (1)th island are connected by an underwater railway.
YJC decides to pull down some of the roads and railways, making every two cities connected directly or indirectly by at most one unique route or not connected at all. Now YJC wants to know how many ways there are to reach his goal. The answer can be huge, so output the answer modulo (998244353).
(1leq Tleq 5,nleq 100000,1leq {a}_{i}leq 100000)
题解
- 令 (f_i) 表示 (i) 个点的有标号树的个数。
- 令 (g_i) 表示 (i) 个点的有标号森林的个数。
- 令 (h_i) 表示 (i) 个点且 (1) 号点与 (i) 号点不连通,有标号的森林的个数。
那么答案应为
先笼统地算一下:岛屿的完全图拆成森林,海底隧道可拆可不拆。
容易发现不合法的情况:海底隧道都不拆,所有岛屿中 (1) 号点与 (a_i) 号点联通。
简单容斥。
令 (F(x),G(x),H(x)) 为 (f,g,h) 的指数型生成函数。
之前一直背的结论:有标号的问题用指数型生成函数。
那么首先有
回忆 (e^x=sum_{i=0}^infty frac{x^i}{i!}),(F(x)^i) 表示的是选了 (i) 棵树的生成函数。
那么此时指数型生成函数的优势就体现出来了,它相当于给你自动乘了一个组合数。
其次,考虑计算 (h_i),有
这里用了一个套路:枚举与 (1) 号点连通的点的个数。
因此
这里跨度有点大,还是从上面就散 (h_i) 式子下手。
[h_i=sum_{j=1}^{i-1} inom{i-2}{j-1}f_jg_{i-j} ]改为枚举 (j-1)。
[h_i=sum_{j=0}^{i-2}inom{i-2}{j}f_{j+1}g_{i-j-1}\ frac{h_{i}}{(i-2)!}=sum_{j=0}^{i-2}frac{f_{j+1}}{j!}frac{g_{i-j-1}}{(i-j-2)!} ]注意观察分子分母的下标,发现只要 (h) 左移两位,(f,g) 左移一位,那么他们的指数型生成函数就对上了。
指数型生成函数求导 (leftrightarrow) 系数左移。
指数型生成函数积分 (leftrightarrow) 系数右移。
进而
时间复杂度 (O(n log n))。
CO int N=262144+10;
int fac[N],inv[N],ifac[N];
void NTT(poly&a,int dir){
static int rev[N],omg[N];
int lim=a.size(),len=log2(lim);
for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
omg[0]=1,omg[1]=fpow(dir==1?3:332748118,(mod-1)/lim);
for(int i=2;i<lim;++i) omg[i]=mul(omg[i-1],omg[1]);
for(int i=1;i<lim;i<<=1)
for(int j=0;j<lim;j+=i<<1)
for(int k=0;k<i;++k){
int t=mul(omg[lim/(i<<1)*k],a[j+i+k]);
a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
}
if(dir==-1){
for(int i=0;i<lim;++i) a[i]=mul(a[i],inv[lim]);
}
}
poly inver(poly a){
int n=a.size();
poly b(1,fpow(a[0],mod-2));
if(n==1) return b;
int lim=2;
for(;lim<n;lim<<=1){
poly a1(a.begin(),a.begin()+lim);
a1.resize(lim<<1),NTT(a1,1);
b.resize(lim<<1),NTT(b,1);
for(int i=0;i<lim<<1;++i) b[i]=mul(add(2,mod-mul(a1[i],b[i])),b[i]);
NTT(b,-1),b.resize(lim);
}
a.resize(lim<<1),NTT(a,1);
b.resize(lim<<1),NTT(b,1);
for(int i=0;i<lim<<1;++i) b[i]=mul(add(2,mod-mul(a[i],b[i])),b[i]);
NTT(b,-1),b.resize(n);
return b;
}
poly differ(CO poly&a){
poly b(a.size()-1);
for(int i=0;i<(int)b.size();++i) b[i]=mul(a[i+1],i+1);
return b;
}
poly inter(CO poly&a){
poly b(a.size()+1);
for(int i=1;i<(int)b.size();++i) b[i]=mul(a[i-1],inv[i]);
return b;
}
poly log(poly a){
int n=a.size();
poly b=inver(a);
a=differ(a);
int lim=1<<int(ceil(log2(2*n-2)));
a.resize(lim),NTT(a,1);
b.resize(lim),NTT(b,1);
for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
NTT(a,-1),a.resize(n);
a=inter(a),a.resize(n);
return a;
}
poly exp(poly a){
int n=a.size();
poly b(1,1);
if(n==1) return b;
int lim=2;
for(;lim<n;lim<<=1){
poly a1(a.begin(),a.begin()+lim);
a1.resize(lim<<1),NTT(a1,1);
b.resize(lim);poly b1=log(b);
b1.resize(lim<<1),NTT(b1,1);
b.resize(lim<<1),NTT(b,1);
for(int i=0;i<lim<<1;++i) b[i]=mul(add(1,add(a1[i],mod-b1[i])),b[i]);
NTT(b,-1),b.resize(lim);
}
a.resize(lim<<1),NTT(a,1);
b.resize(lim);poly b1=log(b);
b1.resize(lim<<1),NTT(b1,1);
b.resize(lim<<1),NTT(b,1);
for(int i=0;i<lim<<1;++i) b[i]=mul(add(1,add(a[i],mod-b1[i])),b[i]);
NTT(b,-1),b.resize(n);
return b;
}
poly operator*(poly a,poly b){
int n=a.size(),m=b.size();
int lim=1<<int(ceil(log2(n+m-1)));
a.resize(lim),NTT(a,1);
b.resize(lim),NTT(b,1);
for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
NTT(a,-1),a.resize(n+m-1);
return a;
}
poly tree,forest,divorce;
void real_main(){
static int a[N];
int n=read<int>();
for(int i=1;i<=n;++i) read(a[i]);
a[0]=a[n];
int ans=fpow(2,n),now=1;
for(int i=1;i<=n;++i){
ans=mul(ans,forest[a[i]]);
now=mul(now,add(forest[a[i]],mod-divorce[a[i]]));
}
printf("%d
",add(ans,mod-now));
}
int main(){
fac[0]=1;
for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
inv[0]=inv[1]=1;
for(int i=2;i<N;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
ifac[0]=1;
for(int i=1;i<N;++i) ifac[i]=mul(ifac[i-1],inv[i]);
int n=1e5;
tree.resize(n+1),tree[0]=0,tree[1]=1;
for(int i=2;i<=n;++i) tree[i]=mul(fpow(i,i-2),ifac[i]);
forest=exp(tree);
poly dtree=differ(tree),dforest=differ(forest);
poly dddivorce=dtree*dforest;dddivorce.resize(n+1);
divorce=inter(inter(dddivorce));
for(int i=0;i<=n;++i){
forest[i]=mul(forest[i],fac[i]);
divorce[i]=mul(divorce[i],fac[i]);
}
for(int T=read<int>();T--;) real_main();
return 0;
}