• 有标号的DAG计数系列问题


    传送门

    II

    (f_i) 表示 (i) 个点的答案
    那么枚举至少 (j) 个点的出度为 (0)

    [sum_{j=0}^{i}(-1)^jinom{i}{j}f_{i-j}2^{(i-j)j}=0 ]

    所以

    [f_i=sum_{j=1}^{i}(-1)^{j+1}inom{i}{j}f_{i-j}2^{(i-j)j} ]

    [frac{f_i}{i!}=sum_{j=0}^{i-1}frac{f_j}{j!}frac{(-1)^{i-j+1}}{(i-j)!}2^{(i-j)j} ]

    现在的难处在于 (2^{(i-j)j}) 的拆分
    可以发现

    [2^{(i-j)j}=frac{2^{frac{i^2}{2}}}{2^{frac{(i-j)^2}{2}}2^{frac{j^2}{2}}} ]

    所以

    [frac{f_i}{i!sqrt{2}^{i^2}}=sum_{j=0}^{i-1}frac{f_j}{j!sqrt{2}^{j^2}}frac{(-1)^{i-j+1}}{(i-j)!sqrt{2}^{(i-j)^2}} ]

    求出 (2) 在模 (998244353) 意义下的二次剩余然后多项式求逆即可
    二次剩余没有学QwQ直接暴力预处理出来即可

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn(4e5 + 5);
    const int mod(998244353);
    const int res2(116195171);
    const int invres2(557219762);
    
    inline void Inc(int &x, int y) {
    	if ((x += y) >= mod) x -= mod;
    }
    
    inline int Pow(ll x, int y) {
    	register ll ret = 1;
    	for (; y; y >>= 1, x = x * x % mod)
    		if (y & 1) ret = ret * x % mod;
    	return ret;
    }
    
    int a[maxn], b[maxn], w[2][maxn], deg, r[maxn], l;
    
    inline void Init(int n) {
        register int i, k, wn, iwn;
        for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
        for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
        for (i = 1; i < deg; i <<= 1) {
            w[0][0] = w[1][0] = 1;
            wn = Pow(3, (mod - 1) / (i << 1)), iwn = Pow(wn, mod - 2);
            for (k = 1; k < i; ++k) {
                w[0][deg / i * k] = 1LL * w[0][deg / i * (k - 1)] * wn % mod;
                w[1][deg / i * k] = 1LL * w[1][deg / i * (k - 1)] * iwn % mod;
            }
        }
    }
    
    inline void NTT(int *p, int opt) {
        register int i, j, k, t, wn, x, y;
        for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
        for (i = 1; i < deg; i <<= 1)
            for(t = i << 1, j = 0; j < deg; j += t)
                for (k = 0; k < i; ++k) {
                    wn = w[opt == -1][deg / i * k];
                    x = p[j + k], y = 1LL * wn * p[i + j + k] % mod;
                    p[j + k] = x + y, p[i + j + k] = x - y;
                    if (p[j + k] >= mod) p[j + k] -= mod;
                    if (p[i + j + k] < 0) p[i + j + k] += mod;
                }
        if (opt == -1) {
            wn = Pow(deg, mod - 2);
            for (i = 0; i < deg; ++i) p[i] = 1LL * p[i] * wn % mod;
        }
    }
    
    int n, f[maxn], g[maxn], fac[maxn], ifac[maxn];
    
    void Inv(int *p, int *q, int len) {
        if (len == 1) {
            q[0] = Pow(p[0], mod - 2);
            return;
        }
        Inv(p, q, len >> 1);
        register int i, tmp = len << 1;
        for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
        Init(tmp), NTT(a, 1), NTT(b, 1);
        for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * b[i] % mod * b[i] % mod;
        NTT(a, -1);
        for (i = 0; i < len; ++i) q[i] = (2LL * q[i] + mod - a[i]) % mod;
        for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
    }
    
    int main() {
    	register int i, len;
    	scanf("%d", &n), f[0] = fac[0] = ifac[0] = ifac[1] = 1;
    	for (i = 2; i <= n; ++i) ifac[i] = 1LL * (mod - mod / i) * ifac[mod % i] % mod;
    	for (i = 1; i <= n; ++i) fac[i] = 1LL * fac[i - 1] * i % mod, ifac[i] = 1LL * ifac[i - 1] * ifac[i] % mod;
    	for (i = 0; i <= n; ++i) {
    		f[i] = 1LL * ifac[i] * Pow(invres2, 1LL * i * i % (mod - 1)) % mod;
    		if (i & 1) f[i] = mod - f[i];
    	}
    	for (len = 1; len <= n; len <<= 1);
    	Inv(f, g, len);
    	printf("%lld
    ", 1LL * fac[n] * Pow(res2, 1LL * n * n % (mod - 1)) % mod * g[n] % mod);
    	return 0;
    }
    

    IV

    (f_i) 表示不要求连通的 (i) 个点 的 (DAG) 的方案数
    直接 (II) 那样求出来
    (g_i) 表示 (i) 个点的答案

    方法一

    钦定第一个连通块的一个点

    [g_i=f_i-sum_{j=1}^{i-1}g_jf_{i-j}inom{i-1}{j-1} ]

    组合数拆整理得到

    [frac{f_i}{(i-1)!}=sum_{j=1}^{i}frac{g_j}{(i-1)!}frac{f_{i-j}}{(i-j)!} ]

    直接多项式求逆后卷起来就好了

    方法二

    [F(x)=sum_{i=0}^{infty}f_ifrac{x^i}{i!} ]

    [G(x)=sum_{i=0}^{infty}g_ifrac{x^i}{i!} ]

    那么

    [F(x)=e^{G(x)},G(x)=ln F(x) ]

    方法一

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn(4e5 + 5);
    const int mod(998244353);
    const int res2(116195171);
    const int invres2(557219762);
    
    inline void Inc(int &x, int y) {
    	if ((x += y) >= mod) x -= mod;
    }
    
    inline int Pow(ll x, int y) {
    	register ll ret = 1;
    	for (; y; y >>= 1, x = x * x % mod)
    		if (y & 1) ret = ret * x % mod;
    	return ret;
    }
    
    int a[maxn], b[maxn], w[2][maxn], deg, r[maxn], l;
    
    inline void Init(int n) {
        register int i, k, wn, iwn;
        for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
        for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
        for (i = 1; i < deg; i <<= 1) {
            w[0][0] = w[1][0] = 1;
            wn = Pow(3, (mod - 1) / (i << 1)), iwn = Pow(wn, mod - 2);
            for (k = 1; k < i; ++k) {
                w[0][deg / i * k] = 1LL * w[0][deg / i * (k - 1)] * wn % mod;
                w[1][deg / i * k] = 1LL * w[1][deg / i * (k - 1)] * iwn % mod;
            }
        }
    }
    
    inline void NTT(int *p, int opt) {
        register int i, j, k, t, wn, x, y;
        for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
        for (i = 1; i < deg; i <<= 1)
            for(t = i << 1, j = 0; j < deg; j += t)
                for (k = 0; k < i; ++k) {
                    wn = w[opt == -1][deg / i * k];
                    x = p[j + k], y = 1LL * wn * p[i + j + k] % mod;
                    p[j + k] = x + y, p[i + j + k] = x - y;
                    if (p[j + k] >= mod) p[j + k] -= mod;
                    if (p[i + j + k] < 0) p[i + j + k] += mod;
                }
        if (opt == -1) {
            wn = Pow(deg, mod - 2);
            for (i = 0; i < deg; ++i) p[i] = 1LL * p[i] * wn % mod;
        }
    }
    
    int n, f[maxn], g[maxn], h[maxn], fac[maxn], ifac[maxn];
    
    void Inv(int *p, int *q, int len) {
        if (len == 1) {
            q[0] = Pow(p[0], mod - 2);
            return;
        }
        Inv(p, q, len >> 1);
        register int i, tmp = len << 1;
        for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
        Init(tmp), NTT(a, 1), NTT(b, 1);
        for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * b[i] % mod * b[i] % mod;
        NTT(a, -1);
        for (i = 0; i < len; ++i) q[i] = (2LL * q[i] + mod - a[i]) % mod;
        for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
    }
    
    int main() {
    	register int i, len;
    	scanf("%d", &n), f[0] = fac[0] = ifac[0] = ifac[1] = 1;
    	for (i = 2; i <= n; ++i) ifac[i] = 1LL * (mod - mod / i) * ifac[mod % i] % mod;
    	for (i = 1; i <= n; ++i) fac[i] = 1LL * fac[i - 1] * i % mod, ifac[i] = 1LL * ifac[i - 1] * ifac[i] % mod;
    	for (i = 0; i <= n; ++i) {
    		f[i] = 1LL * ifac[i] * Pow(invres2, 1LL * i * i % (mod - 1)) % mod;
    		if (i & 1) f[i] = mod - f[i];
    	}
    	for (len = 1; len <= n; len <<= 1);
    	Inv(f, g, len);
    	for (i = 0; i <= n; ++i) g[i] = 1LL * g[i] * Pow(res2, 1LL * i * i % (mod - 1)) % mod;
    	for (memset(f, 0, sizeof(f)), i = n + 1; i < len; ++i) g[i] = 0;
    	for (i = 1; i <= n; ++i) f[i] = 1LL * g[i] * i % mod;
    	Inv(g, h, len), len <<= 1;
        for (i = 0; i < len; ++i) a[i] = f[i], b[i] = h[i];
        Init(len), NTT(a, 1), NTT(b, 1);
        for (i = 0; i < len; ++i) a[i] = 1LL * a[i] * b[i] % mod;
        NTT(a, -1);
    	printf("%lld
    ", 1LL * a[n] * fac[n - 1] % mod);
    	return 0;
    }
    
  • 相关阅读:
    left join 多表关联原理
    自适应居中n中方法
    docker 安装 卸载 Kubernetes安装
    jquery动态生成二维码图片,点击居中展示大图
    shiro 提供对外接口 实现一致性权限
    margin:0 auto解释;图片点击放大全屏
    编程总结
    eclipse插件安装和卸载方式
    ERROR org.hibernate.search.exception.impl.LogErrorHandler
    spring依赖注入和控制反转
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/10078167.html
Copyright © 2020-2023  润新知