「Luogu4233」射命丸文的笔记-生成函数+多项式求逆

Description

链接

Solution

考虑所有竞赛图的哈密顿回路条数 n ! n 2 C n 2 − n \frac {n!} {n} 2^{C_{n}^{2}-n} nn!2Cn2n,即选出一条哈密顿回路剩下的边任意连。

但题目中所求的是有哈密顿回路条数的竞赛图,即强联通的竞赛图。

f ( n ) f(n) f(n)表示点数为 n n n的强联通竞赛图数量。 g ( n ) g(n) g(n)表示点数为 n n n的竞赛图数量。得到

f ( n ) = g ( n ) − ∑ i = 1 n − 1 C n i f ( i ) g ( n − i ) f(n)=g(n)-\sum_{i=1}^{n-1}C_{n}^if(i)g(n-i) f(n)=g(n)i=1n1Cnif(i)g(ni)

即枚举拓扑序最小的联通块,这部分没有出边,剩下的任意连边。

F ( i ) F(i) F(i) f ( i ) i ! \frac {f(i)} {i!} i!f(i)的生成函数, G ( i ) G(i) G(i) g ( i ) i ! \frac {g(i)} {i!} i!g(i)的生成函数,拆开组合数化解后

F ( x ) = G ( x ) G ( x ) + 1 F(x)=\frac {G(x)} {G(x)+1} F(x)=G(x)+1G(x)

多项式求逆即可。

#include <bits/stdc++.h>
using namespace std;

typedef long long lint;
const int mod = 998244353;
const int maxn = 500005;

int m, n;
int fac[maxn], ifac[maxn], G[maxn], H[maxn], F[maxn];

inline int Pow(int x, int k)
{
    int res = 1;
    while (k) {
        if (k & 1) res = (lint)res * x % mod;
        x = (lint)x * x % mod; k >>= 1;
    }
    return res;
}

inline int gi()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int sum = 0;
    while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
    return sum;
}

namespace poly
{

    const int phi = mod - 1, G = 3;
    int n, len, L, R[maxn], A[maxn], B[maxn];

    void NTT(int *a, int f)
    {
        for (int i = 0; i < n; ++i) if (i < R[i]) swap(a[i], a[R[i]]);
        for (int i = 1; i < n; i <<= 1) {
            int wn = Pow(G, phi / (i << 1)), t;
            if (f == -1) wn = Pow(wn, mod - 2);
            for (int j = 0; j < n; j += (i << 1)) {
                int w = 1;
                for (int k = 0; k < i; ++k, w = (lint)w * wn % mod) {
                    t = (lint)a[j + i + k] * w % mod;
                    a[j + i + k] = a[j + k] - t;
                    if (a[j + i + k] < 0) a[j + i + k] += mod;
                    a[j + k] = a[j + k] + t;
                    if (a[j + k] >= mod) a[j + k] -= mod;
                }
            }
        }
    }

    void mul(int *a, int *b, int len1, int len2, int *c, int len3)
    {
        for (L = 0, n = 1, len = len1 + len2 - 1; n < len; n <<= 1) ++L;
        for (int i = 0; i < n; ++i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));

        fill(A, A + n, 0); fill(B, B + n, 0);
        for (int i = 0; i < len1; ++i) A[i] = a[i];
        for (int i = 0; i < len2; ++i) B[i] = b[i];
        NTT(A, 1); NTT(B, 1);
        for (int i = 0; i < n; ++i) A[i] = (lint)A[i] * B[i] % mod;
        NTT(A, -1);

        int inv = Pow(n, mod - 2);
        for (int i = 0; i < len3; ++i) c[i] = (lint)A[i] * inv % mod;
    }

    void mul(int *a, int *b, int len1, int *c, int len2)
    {
        for (L = 0, n = 1, len = len1 + len1 - 1; n < len; n <<= 1) ++L;
        for (int i = 0; i < n; ++i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));

        fill(A, A + n, 0); fill(B, B + n, 0);
        for (int i = 0; i < len1; ++i) A[i] = a[i];
        for (int i = 0; i < len2; ++i) B[i] = b[i];
        NTT(A, 1); NTT(B, 1);
        for (int i = 0; i < n; ++i) A[i] = (lint)A[i] * B[i] % mod * B[i] % mod;
        NTT(A, -1);

        int inv = Pow(n, mod - 2);
        for (int i = 0; i < len2; ++i) c[i] = (lint)A[i] * inv % mod;
    }

    void poly_inv(int *a, int n, int *b)
    {
        static int c[maxn];
        b[0] = Pow(a[0], mod - 2);
        for (int t = 2; (t >> 1) < n; t <<= 1) {
            for (int i = 0; i < t; ++i) c[i] = b[i];
            mul(a, b, t, b, t);
            for (int i = 0; i < t; ++i) b[i] = ((c[i] << 1ll) - b[i] + mod) % mod;
            fill(c, c + t, 0);
        }
    }
    
}
    
int main()
{
    m = gi();
    for (n = 1; n <= m; n <<= 1) ;

    fac[0] = ifac[0] = ifac[1] = 1;
    for (int i = 2; i < n; ++i) ifac[i] = (lint)(mod - mod / i) * ifac[mod % i] % mod;
    for (int i = 2; i < n; ++i) ifac[i] = (lint)ifac[i - 1] * ifac[i] % mod;
    for (int i = 1; i < n; ++i) fac[i] = (lint)fac[i - 1] * i % mod;

    for (int i = 1; i < n; ++i) G[i] = H[i] = (lint)Pow(2, ((lint)i * (i - 1) / 2) % (mod - 1)) * ifac[i] % mod;
    ++H[0];
    poly::poly_inv(H, n, F);
    poly::mul(G, F, n, n, F, n);

    for (int ans, i = 1; i <= m; ++i) {
        if (i == 2) {puts("-1"); continue;}
        F[i] = (lint)F[i] * fac[i] % mod;
        ans = (lint)fac[i - 1] * Pow(2, max(0ll, ((lint)i * (i - 1) / 2 - i)) % (mod - 1)) % mod * Pow(F[i], mod - 2) % mod;
        printf("%d\n", ans);
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值