題目鏈接
題解
做了 8 個小時,最后搞出來一個極其麻煩的做法。。字母用光警告
首先考慮一下圖論背景的轉化:
這個模型相當於 \(n\) 個圖上同時游走,每次在每個圖上走一步,如果從一個狀態可達另一個狀態則它們連通。
性質 1:如果狀態 \(u=(u_1,u_2,...,u_n)\) 和 \(v=(v_1,v_2,...,v_n)\) 滿足存在 \(k\) 使得在圖 \(k\) 中 \(u_k\) 和 \(v_k\) 不連通,則狀態 \(u\) 和狀態 \(v\) 不連通。特別地,對於一個孤立點而言,它不和自己連通。
那么我們就可以從每個圖各取出一個連通塊考慮,再把所有的方案的答案加起來。特別地,除特殊說明外,孤立點不算作連通塊/連通圖。
性質 2:狀態 \(u\) 和狀態 \(v\) 連通當且僅當存在 \(l\) 使 \(\forall k\),在圖 \(k\) 中存在從 \(u_k\) 到 \(v_k\) 的長度為 \(l\) 的路徑。
性質 3:對於一張無向連通圖而言,若圖中存在 \(u\) 到 \(v\) 長度為 \(l\) 的路徑,則存在 \(u\) 到 \(v\) 長度為 \(l+2k\ (k\in \mathbb{Z})\) 的路徑。
這意味着我們考慮路徑長度時只需要考慮它的奇偶性,如果奇偶性相同,那么當路徑長度足夠大時一定能滿足長度相同。
性質 4:狀態 \(u\) 和狀態 \(v\) 連通當且僅當存在 \(l\in\{0,1\}\) 使 \(\forall k\),在圖 \(k\) 中存在從 \(u_k\) 到 \(v_k\) 的長度 \(\mod 2=l\) 的路徑。
性質 5:對於一張無向連通圖而言,對於任意的 \(u,v\),若該圖是二分圖,則 \(u\) 和 \(v\) 之間只存在一種奇偶性的長度;否則,長度為奇數和偶數的路徑都存在。
性質 6:回到原題,假設 \(n\) 個圖都是連通的,設 \(n\) 個圖中有 \(b\) 個圖是二分圖,對答案的貢獻是 \(2^{\max(0,b-1)}\).
那么 \(n\) 個圖(不一定都要連通),對答案的貢獻就是
后面一部分的含義是,先不考慮和 \(0\) 取 \(\max\) 的問題,相當於每取一個二分圖連通塊權值要乘以 \(2\),最后再除以 \(2\). 這樣會導致沒有二分圖的情況只被算了半次,那么就把它加上。
因此我們可以分這四部分計算答案,分別計算總點數、孤立點數、連通塊數、二分圖連通塊數的乘積之和。每部分是 \(n\) 個圖的 \(\prod\) 的形式,所以可以對每個圖求出它所有形態下的和,然后乘起來。
接下來就是生成函數一頓爆推,用小寫字母表示一個序列,對應的大寫字母表示其對應的指數生成函數。
(生成函數的式子和 DP 相比異常簡潔,然而最開始我全都是拿 DP 推的……)
(1) 總點數
用 \(F\) 表示 \(n\) 個點的圖的總點數。則有
(2) 孤立點數
用 \(G\) 表示 \(n\) 個點的圖的總孤立點數。則有
(3) 連通塊數
用 \(E\) 表示 \(n\) 個點的圖的個數。則有
用 \(C\) 表示 \(n\) 個點連通圖(含孤立點)的個數。
一個圖是由若干個連通塊(含孤立點)彼此之間無序構成的,因此
用 \(D\) 表示 \(n\) 個點的所有圖中連通塊數的總和。
先求出沒有孤立點的時候的答案,再把孤立點的貢獻 \(e^x\) 乘上
(4) 二分圖連通塊數
這部分是重點。
先考慮如何計算 \(n\) 個點二分圖的個數。
考慮黑白染色,一個連通(含孤立點)的二分圖一定只有 \(2\) 種黑白染色方案,一個二分圖的黑白染色方案數量是 \(2\) 的連通塊個數次冪。
用 \(H\) 表示對 \(n\) 個點的圖進行黑白染色,\((染色方案,該方案對應的二分圖)\) 這個二元組的個數。
這個的計算可以把 \(i(n-i)\) 拆成 \({n\choose 2}-{i\choose 2}-{n-i\choose 2}\),然后 NTT.
用 \(R\) 表示 \(n\) 個點的連通二分圖(含孤立點)的個數。這其實可以視為 \((染色方案,該方案對應的連通二分圖)\) 二元組的個數乘以 \(\frac{1}{2}\).
用 \(B\) 表示 \(n\) 個點的二分圖的個數。
神奇的事情來了:一個二分圖也是由若干連通塊(含孤立點)彼此之間無序構成的。而且每個連通塊有 \(2\) 種染色方案相當於每加一個連通塊要乘以 \(2\) 的權值,所以
(所以如果只是要求二分圖的個數的話,直接 \(B=\sqrt H\) 就可以了。)
用 \(S\) 表示 \(n\) 個點的圖總共有多少個二分圖連通塊。先求出 \((R-x)e^{R-x}\) 表示 \(n\) 個點、每個連通塊都是二分圖(不含孤立點)的圖總連通塊個數,再乘上其余的非二分圖連通塊(含孤立點)。
最后答案就等於 \(\prod f_{m_i}-\prod g_{m_i}+\prod (d_{m_i}+s_{m_i})+\prod (d_{m_i}-s_{m_i})\)
於是終於做完了,時間復雜度 \(O(n\log n)\). 實際上這里並沒有求 exp 的必要,所以常數並沒有看上去那么大,至少 600ms 之內能跑出來。
盡管我盡力避免用重字母,最后還是重了一個 \(e\)/kk
代碼
#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define x first
#define y second
#define iter iterator
#define riter reverse_iterator
#define y1 Lorem_ipsum_
#define tm dolor_sit_amet_
using namespace std;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
return x*f;
}
const int mxN = 1<<19;
const int lgN = 19;
const int P = 998244353;
llong fact[mxN+3],facti[mxN+3],inv[mxN+3],pwc2[mxN+3];
llong quickpow(llong x,llong y)
{
llong cur = x,ret = 1ll;
for(int i=0; y; i++)
{
if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}
cur = cur*cur%P;
}
return ret;
}
llong mulinv(llong x) {return quickpow(x,P-2);}
llong comb(llong x,llong y) {return x<0ll||y<0ll||x<y?0ll:fact[x]*facti[y]%P*facti[x-y]%P;}
void initfact()
{
fact[0] = 1ll; for(int i=1; i<=mxN; i++) fact[i] = fact[i-1]*i%P;
facti[mxN] = quickpow(fact[mxN],P-2); for(int i=mxN-1; i>=0; i--) facti[i] = facti[i+1]*(i+1ll)%P;
for(int i=1; i<=mxN; i++) inv[i] = facti[i]*fact[i-1]%P;
for(int i=0; i<=mxN; i++) pwc2[i] = quickpow(2ll,1ll*i*(i-1ll)/2ll);
}
namespace FFT
{
const int G = 3;
llong aux1[mxN+3],aux2[mxN+3],aux3[mxN+3],aux4[mxN+3],aux5[mxN+3],aux6[mxN+3],aux7[mxN+3];
int fftid[mxN+3]; llong sexp[mxN+3];
int getdgr(int n) {int ret = 1; while(ret<=n) ret<<=1; return ret;}
void init_fftid(int dgr)
{
for(int i=1; i<dgr; i++) fftid[i] = (fftid[i>>1]>>1)|((i&1)*(dgr>>1));
}
void ntt(int dgr,int coe,llong poly[],llong ret[])
{
init_fftid(dgr);
if(poly==ret) {for(int i=0; i<dgr; i++) if(i<fftid[i]) swap(ret[i],ret[fftid[i]]);}
else {for(int i=0; i<dgr; i++) ret[i] = poly[fftid[i]];}
for(int i=1; i<dgr; i<<=1)
{
llong tmp = quickpow(G,(P-1)/(i<<1)); if(coe==-1) {tmp = mulinv(tmp);}
sexp[0] = 1ll; for(int j=1; j<i; j++) sexp[j] = sexp[j-1]*tmp%P;
for(int j=0; j<dgr; j+=(i<<1))
{
for(llong *k=ret+j,*kk=sexp; k<ret+i+j; k++,kk++)
{
llong x = *k,y = (*kk)*k[i]%P;
*k = x+y>=P?x+y-P:x+y;
k[i] = x-y<0ll?x-y+P:x-y;
}
}
}
if(coe==-1)
{
llong tmp = mulinv(dgr); for(int i=0; i<dgr; i++) ret[i] = ret[i]*tmp%P;
}
}
void polymul(int dgr,llong poly1[],llong poly2[],llong ret[])
{
memset(poly1+dgr,0,sizeof(llong)*dgr); memset(poly2+dgr,0,sizeof(llong)*dgr);
ntt(dgr<<1,1,poly1,aux1); ntt(dgr<<1,1,poly2,aux2);
for(int i=0; i<(dgr<<1); i++) ret[i] = aux1[i]*aux2[i]%P;
ntt(dgr<<1,-1,ret,ret);
}
void polyinv(int dgr,llong poly[],llong ret[])
{
memset(ret,0,sizeof(llong)*(dgr<<1)); ret[0] = mulinv(poly[0]);
for(int i=1; i<dgr; i<<=1)
{
ntt(i<<2,1,ret,aux3);
for(int j=0; j<(i<<2); j++) aux4[j] = j<(i<<1)?poly[j]:0ll;
ntt(i<<2,1,aux4,aux4);
for(int j=0; j<(i<<2); j++) aux4[j] = (2ll*aux3[j]-aux3[j]*aux3[j]%P*aux4[j]%P+P)%P;
ntt(i<<2,-1,aux4,aux4);
for(int j=0; j<(i<<1); j++) ret[j] = aux4[j];
}
}
void polyln(int dgr,llong poly[],llong ret[])
{
for(int i=dgr-2; i>=0; i--) aux5[i] = poly[i+1]*(i+1ll)%P; aux5[dgr-1] = 0ll;
polyinv(dgr,poly,aux6);
polymul(dgr,aux5,aux6,aux7);
ret[0] = 0ll; for(int i=1; i<dgr; i++) ret[i] = aux7[i-1]*inv[i]%P;
}
}
using FFT::ntt;
using FFT::getdgr;
using FFT::polymul;
using FFT::polyinv;
using FFT::polyln;
int dgr,q;
int n[mxN+3];
llong f[mxN+3],g[mxN+3],e[mxN+3],c[mxN+3],d[mxN+3],h[mxN+3],r[mxN+3],s[mxN+3],w[mxN+3];
llong aux1[mxN+3],aux2[mxN+3],aux3[mxN+3],aux4[mxN+3];
int main()
{
initfact();
q = read(); for(int i=1; i<=q; i++) {n[i] = read(); dgr = max(dgr,n[i]);}
dgr = getdgr(dgr);
for(int i=0; i<dgr; i++) {f[i] = pwc2[i]*i%P,g[i] = (pwc2[i]-pwc2[i-1]+P)*i%P;}
for(int i=0; i<dgr; i++) {e[i] = pwc2[i]*facti[i]%P;}
polyln(dgr,e,c); c[1] = 0ll;
polymul(dgr,c,e,d); memset(d+dgr,0,sizeof(llong)*dgr);
for(int i=0; i<dgr; i++) aux1[i] = facti[i]; memset(d+dgr,0,sizeof(llong)*dgr);
for(int i=0; i<dgr; i++) {aux3[i] = mulinv(pwc2[i]*fact[i]%P);}
ntt(dgr<<1,1,aux3,aux3); for(int i=0; i<(dgr<<1); i++) aux3[i] = aux3[i]*aux3[i]%P; ntt(dgr<<1,-1,aux3,aux3);
for(int i=0; i<dgr; i++) {h[i] = aux3[i]*pwc2[i]%P;}
polyln(dgr,h,r); r[1] = 0ll; for(int i=0; i<dgr; i++) r[i] = r[i]*inv[2]%P;
polymul(dgr,r,e,s); memset(s+dgr,0,sizeof(llong)*dgr);
llong ans1 = 1ll,ans2 = 1ll,ans3 = 1ll,ans4 = 1ll;
for(int i=1; i<=q; i++)
{
ans1 = ans1*f[n[i]]%P,ans2 = ans2*g[n[i]]%P,
ans3 = ans3*(d[n[i]]+s[n[i]])%P*fact[n[i]]%P,ans4 = ans4*(d[n[i]]-s[n[i]]+P)%P*fact[n[i]]%P;
}
llong ans = (ans1-ans2+(ans3+ans4)*inv[2]+P)%P;
printf("%lld\n",ans);
return 0;
}