21.6.24 t3
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;
}