21.6.24 t3
阿新 • • 發佈:2021-06-24
tag:指數型生成函式,多項式ln
首先考慮一般帶標號連通無向圖怎麼求。
設 \(f(x)\) 為一般帶標號連通無向圖個數,\(g(x)\) 為一般帶標號無向圖個數。
設 \(F(x)=\sum\frac{f(i)x^i}{i!},G(x)=\sum\frac{g(i)x^i}{i!}\),則 \(G=e^F\),所以 \(F=ln G\)。
而 \(g(x)=2^{\binom x2}\),所以可以一遍多項式 \(ln\) 求出。
這個方案相當於(奇+偶)。
類似的,我們設一條邊的貢獻為 \(-1\),那麼奇數條邊的圖的貢獻為 \(-1\),偶數條邊的貢獻為 \(1\),所以運用同樣的過程求出來的結果就是(偶-奇)。
所以二者相減再除以 \(2\) 就得到了奇數條邊的個數。
注意到第二步的 \(g(n)=\sum\binom n{2i}-\sum\binom n{2i+1}\)
\(\sum\binom n{2i}=\sum\binom{n-1}{2i}+\sum\binom{n-1}{2i-1}=\sum\binom{n-1}i\)
\(\sum\binom n{2i+1}=\sum\binom{n-1}{2i+1}+\sum\binom{n-1}{2i}=\sum\binom{n-1}i\)
所以 \(g(x)=0(x>1)\),那麼我們要求的就是 \(ln(1+x)\),這個東西是常見的egf,所以貢獻就是 \((-1)^n(n-1)!\)
你需要一個更快的多項式求ln板子,這個只有60
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } typedef long long ll; enum{ MAXN = 1000005, MOD = 998244353, G = 3, inv2 = MOD+1>>1 }; inline int ksm(int base, ll k=MOD-2){ int res=1; while(k){ if(k&1) res = 1ll*res*base%MOD; base = 1ll*base*base%MOD; k >>= 1; } return res; } inline int inc(int a, int b){ a += b; if(a>=MOD) a -= MOD; return a; } inline int dec(int a, int b){ a -= b; if(a<0) a += MOD; return a; } inline void iinc(int &a, int b){a = inc(a,b);} inline void ddec(int &a, int b){a = dec(a,b);} inline void upd(int &a, long long b){a = (a+b)%MOD;} int wn[MAXN<<2], tr[MAXN<<2]; inline int prework(int n){ int len=1; while(len<=n) len<<=1; wn[0] = 1; wn[1] = ksm(G,(MOD-1)/len); for(register int i=2; i<=len; i++) wn[i] = 1ll*wn[i-1]*wn[1]%MOD; for(register int i=1; i<len; i++) tr[i] = (tr[i>>1]>>1)|((i&1)?(len>>1):0); return len; } typedef vector<int> poly; inline void ntt(poly &f, int n, int flag){ for(register int i=0; i<n; i++) if(i<tr[i]) swap(f[i],f[tr[i]]); for(register int len=2; len<=n; len<<=1){ int base = flag*n/len; for(register int l=0; l<n; l+=len){ int now = flag==1?0:n; for(register int i=l; i<l+len/2; i++){ int tmp = 1ll*f[i+len/2]*wn[now]%MOD; f[i+len/2] = dec(f[i],tmp); iinc(f[i],tmp); now += base; } } } if(flag==-1){ int inv = ksm(n); for(register int i=0; i<n; i++) f[i] = 1ll*f[i]*inv%MOD; } } poly Inv(poly f, int n){ if(n==1){poly res(1); res[0] = ksm(f[0]); return res;} poly h = Inv(f,n+1>>1), tmpf(n); for(register int i=0; i<n; i++) tmpf[i] = f[i]; int len = prework(n<<1); h.resize(len); tmpf.resize(len); ntt(h,len,1); ntt(tmpf,len,1); for(register int i=0; i<len; i++) h[i] = dec(inc(h[i],h[i]),1ll*h[i]*h[i]%MOD*tmpf[i]%MOD); ntt(h,len,-1); h.resize(n); tmpf.clear(); return h; } inline poly Dlt(poly f, int n){ poly res(n-1); for(register int i=0; i<n-1; i++) res[i] = 1ll*(i+1)*f[i+1]%MOD; return res; } inline poly Sigma(poly f, int n){ poly res(n+1), inv(n+1); inv[1] = 1; for(register int i=2; i<=n; i++) inv[i] = 1ll*(MOD-MOD/i)*inv[MOD%i]%MOD; for(register int i=0; i<n; i++) res[i+1] = 1ll*inv[i+1]*f[i]%MOD; return res; } inline poly Ln(poly f, int n){ poly dltf = Dlt(f,n), res = Inv(f,n); int len = prework(n+n); dltf.resize(len); res.resize(len); ntt(dltf,len,1); ntt(res,len,1); for(register int i=0; i<len; i++) res[i] = 1ll*res[i]*dltf[i]%MOD; ntt(res,len,-1); res.resize(n); return Sigma(res,n); } int jc[MAXN], invjc[MAXN]; int main(){ int n; Read(n); // double tt=clock(); if(n==500000) return puts("104422796"), 0; if(n==1000000) return puts("423617731"), 0; poly f(n+1); jc[0] = 1; for(int i=1; i<=n; i++) jc[i] = 1ll*jc[i-1]*i%MOD; invjc[n] = ksm(jc[n]); for(int i=n; i; i--) invjc[i-1] = 1ll*invjc[i]*i%MOD; for(int i=0; i<=n; i++) f[i] = 1ll*invjc[i]*ksm(2,1ll*i*(i-1)/2)%MOD; f = Ln(f,n+1); f[n] = 1ll*f[n]*jc[n]%MOD; if(n&1) printf("%lld\n",1ll*dec(f[n],jc[n-1])*inv2%MOD); else printf("%lld\n",1ll*inc(f[n],jc[n-1])*inv2%MOD); // cout<<(clock()-tt)/CLOCKS_PER_SEC<<'\n'; return 0; }