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;
}
posted @ 2021-06-24 16:16  oisdoaiu  阅读(29)  评论(0编辑  收藏  举报