【LOJ6363】「地底蔷薇」【点双】【指数型生成函数】【扩展拉格朗日反演】【多项式幂函数】
傳送門
題意:給定nnn和集合SSS,求出nnn個點的「所有極大點雙連通分量的大小都在SSS 內」的不同簡單無向連通圖的個數 模 998244353998244353998244353。
n,∑i∈Si≤105n,\sum_{i\in S}i \leq10^5n,∑i∈S?i≤105
道理我都懂,可為啥我百度搜地靈殿ex終符一半都是cookie☆同人OI題
思路挺清奇的
首先注意題目中的點雙定義是不存在割點,也就是說只有兩個點的連通圖也是點雙
同時一個點可能屬于多個點雙
顯然要設出答案的EGF
因為無向圖關系很復雜,所以我們要強行整點東西上去
設[xn]F(x)[x^n]F(x)[xn]F(x)表示 nnn個點 帶標號 有根 滿足題目條件 的方案數 的EGF
有根是指:每張圖欽定一個根,如果兩張圖邊完全相同但根不同也視為不同的圖
其實就是乘一個nnn
考慮怎么表示出F(x)F(x)F(x)
對于一張有根圖,我們把根刪掉,這樣會出現多個連通塊
對于每個連通塊,會發現都會有若干點和根處于同一個點雙內
但是不會有不同連通塊的點處于同一點雙,因為根結點刪去后它們分開了,與定義矛盾
考慮一個連通塊的情況
先考慮根結點所在的點雙大小,發現同一組點組成的點雙也有多種連邊方案
設ana_nan?表示n+1n+1n+1個點構成的帶標號點雙個數
再設出滿足題目要求的點雙的EGF
A(x)=∑i+1∈Saii!xiA(x)=\sum_{i+1\in S}\frac{a_i}{i!}x^iA(x)=i+1∈S∑?i!ai??xi
因為沒有跨連通塊,所以這個連通塊的點雙一定在SSS內
然后將點雙中的所有邊刪掉,這樣點雙中的點兩兩間都不會連通 因為如果連通,由于內部邊都斷完了,只能走外面的點,而經過的點都會在這個點雙內
也就是說,這個大連通塊被分成了若干小連通塊,且連通塊個數等于點雙的大小(不含根),小連通塊的根是原來的點雙中的點
這樣可以寫出一個大連通塊的EGF
∑i=1∞aii!Fi(x)\sum_{i=1}^{\infin}\frac{a_i}{i!}F^i(x)i=1∑∞?i!ai??Fi(x)
也就是A(F(x))A(F(x))A(F(x))
注意:這個過程并沒有選出點雙中有哪些點,只是把連通塊中的點分組并將每組的根按點雙的方式連接起來
然后F(x)F(x)F(x)可以表示成任意個數連通塊組合再加上根
也就是
F(x)=xeA(F(x))F(x)=xe^{A(F(x))}F(x)=xeA(F(x))
假設我們已經求出了A(x)A(x)A(x),我們嘗試算出F(x)F(x)F(x)
F(x)eA(F(x))=x{F(x)\over e^{A(F(x))}}=xeA(F(x))F(x)?=x
設
f(F(x))=F(x)eA(F(x))=xf(F(x))={F(x)\over e^{A(F(x))}}=xf(F(x))=eA(F(x))F(x)?=x
即fff和FFF互為復合逆
而
f(x)=xeA(x)f(x)=\frac{x}{e^{A(x)}}f(x)=eA(x)x?
顯然[x0]A(x)=0[x^0]A(x)=0[x0]A(x)=0,所以可以算出f(x)f(x)f(x),進而用拉格朗日反演算出[xn]F(x)[x^n]F(x)[xn]F(x)
現在的問題是如何求A(x)A(x)A(x)
直接求不好求,考慮構造一個相似的問題用其他方法算一次,再把A(x)A(x)A(x)反推回來
那考慮減少限制 發現減掉在SSS中的限制,也就是任意連通圖,我們也可以用這個思路計算
并且這是一道原題
城市規劃
直接取對數就可以輕松算出答案,豈不美哉?
好,我們仿(fu)照(zhi)這道題,算出任意無向連通圖的EGF G(x)G(x)G(x),并乘一個nnn上個根
現在G(x)G(x)G(x) 已知了
設
B(x)=∑i=1∞aii!xiB(x)=\sum_{i=1}^{\infin} \frac{a_i}{i!}x^iB(x)=i=1∑∞?i!ai??xi
相同的思路,得到
G(x)=xeB(G(x))G(x)=xe^{B(G(x))}G(x)=xeB(G(x))
G(x)eB(G(x))=x\frac{G(x)}{e^{B(G(x))}}=xeB(G(x))G(x)?=x
g(G(x))=G(x)eB(G(x))=xg(G(x))=\frac{G(x)}{e^{B(G(x))}}=xg(G(x))=eB(G(x))G(x)?=x
g(x)=xeB(x)g(x)=\frac{x}{e^{B(x)}}g(x)=eB(x)x?
變一下可以得到
B(x)=ln?(xg(x))B(x)=\ln(\frac{x}{g(x)})B(x)=ln(g(x)x?)
用拉格朗日反演算出g(x)g(x)g(x)……
拉個鬼啊,一次拉格朗日反演就是O(nlogn)O(nlogn)O(nlogn)的,你要算出g(x)g(x)g(x)就要跑nnn次,再怎么也有O(n2)O(n^2)O(n2)了吧
但我們注意到A(x)A(x)A(x)有值的地方很少,而且下標和不超過1e51e51e5,所以如果我們直接拉出A(x)A(x)A(x)就沒有復雜度問題啦
好,整理一下已知條件
g(G(x))=G(x)eB(G(x))=xg(G(x))=\frac{G(x)}{e^{B(G(x))}}=xg(G(x))=eB(G(x))G(x)?=x
B(x)=ln?(xg(x))B(x)=\ln(\frac{x}{g(x)})B(x)=ln(g(x)x?)
閑著沒事把上面代到下面
(注:這里為了統一代碼改一下)
B(G(g(x)))=ln?(xg(x))B(G(g(x)))=\ln(\frac{x}{g(x)})B(G(g(x)))=ln(g(x)x?)
好多括號啊,拆掉里面那個
B(G(x))=ln?(G(x)x)B(G(x))=\ln(\frac{G(x)}{x})B(G(x))=ln(xG(x)?)
停,我說一句
有個叫擴展拉格朗日反演的東西,長這樣:
[xn]H(G(x))=1n[x?1]H′(x)Fn(x)[x^n]H(G(x))=\frac{1}{n}[x^{-1}]\frac{H'(x)}{F^n(x)}[xn]H(G(x))=n1?[x?1]Fn(x)H′(x)?
證明的話在G(F(x))=xG(F(x))=xG(F(x))=x兩邊同時復合一個H(x)H(x)H(x),并令H(G(x))=T(x)H(G(x))=T(x)H(G(x))=T(x),后面就一模一樣了
好繼續
我們強行構造上面的結論,令
H(g(x))=B(x)H(g(x))=B(x)H(g(x))=B(x)
這樣
[xn]B(x)=[xn]H(g(x))=1n[x?1]H′(x)Gn(x)[x^n]B(x)=[x^n]H(g(x))=\frac{1}{n}[x^{-1}]\frac{H'(x)}{G^n(x)}[xn]B(x)=[xn]H(g(x))=n1?[x?1]Gn(x)H′(x)?
就可以把B(x)B(x)B(x)算出來
現在只需要求出H(x)H(x)H(x)
由于
H(g(x))=B(x)H(g(x))=B(x)H(g(x))=B(x)
得到
H(x)=H(g(G(x)))=B(G(x))=ln?(G(x)x)H(x)=H(g(G(x)))=B(G(x))=\ln(\frac{G(x)}{x})H(x)=H(g(G(x)))=B(G(x))=ln(xG(x)?)
并不需要手動算,除出來O(n)O(n)O(n)挪一下就可以了
把讀入的位置拉出來直接丟到A(x)A(x)A(x)的對應位置,然后推出F(x)F(x)F(x)即可
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <ctime> #define MAXN 400005 using namespace std; const int MOD=998244353; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } typedef long long ll; inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;} inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;} inline int qpow(int a,int p) {int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans; } #define inv(x) qpow(x,MOD-2) int fac[MAXN],finv[MAXN]; int rt[2][24]; int l,r[MAXN]; inline void init(){for (int i=0;i<(1<<l);i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));} inline void NTT(int* a,int type) {int lim=1<<l;for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1,Wn=rt[type][L+1];for (int s=0;s<lim;s+=len)for (int k=0,w=1;k<mid;k++,w=(ll)w*Wn%MOD){int x=a[s+k],y=(ll)w*a[s+mid+k]%MOD;a[s+k]=add(x,y);a[s+mid+k]=dec(x,y);}}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;} } void getinv(int* A,int* B,int n) {static int f[MAXN],t[MAXN];if (n==1) return (void)(*B=inv(*A));getinv(A,t,(n+1)>>1);for (l=0;(1<<l)<(n<<1);l++);init();for (int i=0;i<n;i++) f[i]=A[i];for (int i=n;i<(1<<l);i++) f[i]=t[i]=0;NTT(f,0);NTT(t,0);for (int i=0;i<(1<<l);i++) B[i]=(ll)t[i]*dec(2,(ll)f[i]*t[i]%MOD)%MOD;NTT(B,1);for (int i=n;i<(1<<l);i++) B[i]=0; } inline void deriv(int* A,int* B,int n){for (int i=0;i<n-1;i++) B[i]=(ll)A[i+1]*(i+1)%MOD;B[n-1]=0;} inline void integ(int* A,int* B,int n){for (int i=1;i<n;i++) B[i]=(ll)A[i-1]*fac[i-1]%MOD*finv[i]%MOD;B[0]=0;} void getln(int* A,int* B,int n) {static int f[MAXN],g[MAXN];deriv(A,f,n);getinv(A,g,n);for (int i=n;i<(1<<l);i++) f[i]=g[i]=0;NTT(f,0);NTT(g,0);for (int i=0;i<(1<<l);i++) f[i]=(ll)f[i]*g[i]%MOD;NTT(f,1);integ(f,B,n);for (int i=n;i<(1<<l);i++) B[i]=0; } void getexp(int* A,int* B,int n) {static int f[MAXN],g[MAXN];if (n==1) return (void)(*B=1);getexp(A,g,(n+1)>>1);getln(g,f,n);for (int i=0;i<n;i++) f[i]=dec(A[i],f[i]);++f[0];for (int i=n;i<(1<<l);i++) f[i]=g[i]=0;NTT(f,0);NTT(g,0);for (int i=0;i<(1<<l);i++) B[i]=(ll)f[i]*g[i]%MOD;NTT(B,1);for (int i=n;i<(1<<l);i++) B[i]=0; } void getpow(int* A,int* B,int n,int k) {static int t[MAXN];getln(A,t,n);for (int i=0;i<n;i++) t[i]=(ll)t[i]*k%MOD;getexp(t,B,n); } int G[MAXN],H[MAXN],dH[MAXN],A[MAXN],t[MAXN],f[MAXN]; int LangInv(int* F,int n,bool ex=false) {static int f[MAXN],g[MAXN];for (int i=0;i<n;i++) f[i]=F[i+1];f[n]=0;getinv(f,g,n);getpow(g,f,n,n);int ans=0;if (ex) for (int i=0;i<n;i++) ans=add(ans,(ll)dH[i]*f[n-i-1]%MOD);else ans=f[n-1];return (ll)ans*fac[n-1]%MOD*finv[n]%MOD; } time_t START; int main() {fac[0]=1;for (int i=1;i<MAXN;i++) fac[i]=(ll)fac[i-1]*i%MOD;finv[MAXN-1]=inv(fac[MAXN-1]);for (int i=MAXN-2;i>=0;i--) finv[i]=(ll)finv[i+1]*(i+1)%MOD;rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=22;i>=0;i--){rt[0][i]=(ll)rt[0][i+1]*rt[0][i+1]%MOD;rt[1][i]=(ll)rt[1][i+1]*rt[1][i+1]%MOD;}int n=read();for (int i=0;i<=n;i++) t[i]=(ll)qpow(2,((ll)i*(i-1)/2)%(MOD-1))*finv[i]%MOD;getln(t,G,n+1);for (int i=0;i<=n;i++) G[i]=(ll)G[i]*i%MOD;for (int i=0;i<n;i++) t[i]=G[i+1];t[n]=0;getln(t,H,n+1);deriv(H,dH,n+1);for (int s=read();s;s--){int p=read()-1;A[p]=LangInv(G,p,true);}getexp(A,t,n+1);getinv(t,A,n+1);for (int i=1;i<=n;i++) f[i]=A[i-1];f[0]=0;printf("%d\n",(ll)LangInv(f,n)*fac[n-1]%MOD);return 0; }P.S. 如果你用LOJ的C++(NOI) T成了狗,試試C++……
總結
以上是生活随笔為你收集整理的【LOJ6363】「地底蔷薇」【点双】【指数型生成函数】【扩展拉格朗日反演】【多项式幂函数】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 24BIT 音乐
- 下一篇: drbd(一):简介和安装