图计数

有标号无向连通图计数

\(f_n\) 表示大小为 \(n\)有标号无向连通图 方案数,\(g_n\) 表示大小为 \(n\)有标号无向图 方案数,显然有 \(g_n = 2^{\binom{n}{2}}\),通过钦定 \(1\) 号节点所在联通块的大小为 \(i\),它们之间具有关系

\[g_n = \sum_{i = 1}^{n}\binom{n - 1}{i - 1}f_ig_{n - i} \]

也就是说

\[2^{\binom{n}{2}} = \sum_{i = 1}^{n}\binom{n - 1}{i - 1}2^{\binom{n - i}{2}}f_i \]

\[2^{\binom{n}{2}} = \sum_{i = 1}^{n}\frac{(n - 1)!}{(i - 1)!(n - i)!}2^{\binom{n - i}{2}}f_i \]

移项可得

\[\frac{2^{\binom{n}{2}}}{(n - 1)!} = \sum_{i = 1}^{n}\frac{2^{\binom{n - i}{2}}}{(n - i)!}\cdot\frac{f_i}{(i - 1)!} \]

不难看出右式是一个卷积形式,这告诉我们

\[\frac{2^{\binom{n}{2}}}{(n - 1)!}x^n = \sum_{i = 1}^{n}\left(\frac{2^{\binom{n - i}{2}}}{(n - i)!}x^{n - i}\right)\left(\frac{f_i}{(i - 1)!}x^{i}\right) \]

\(F(x) = \sum\limits_{i = 0}^{n - 1}\frac{2^{\binom{i}{2}}}{i!}x^i\)\(G(x) = \sum\limits_{i = 1}^n\frac{f_i}{(i - 1)!}x^i\)\(H(x) = \sum\limits_{i = 1}^{n}\frac{2^{\binom{i}{2}}}{(i - 1)!}x^i\),那么有 \(F(x)G(x) = H(x)\),求逆即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 4e5 + 10, _G = 3, mod = 1004535809;

ll qpow(ll a, ll k = mod - 2) {
    ll res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}

const int invG = qpow(_G);
int rev[N << 1], rev_len;

void rev_init(int n) {
    if (rev_len == n) return;
    for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
    rev_len = n;
}

void NTT(int *g, int op, int n) {
    rev_init(n);
    static ull f[N << 1], Gk[N << 1] = {1};
    for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
    for (int k = 1; k < n; k <<= 1) {
        int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
        for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
        for (int i = 0; i < n; i += k << 1) {
            for (int j = 0; j < k; j ++ ) {
                int tmp = Gk[j] * f[i | j | k] % mod;
                f[i | j | k] = f[i | j] + mod - tmp;
                f[i | j] += tmp;
            }
        }
        if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
    }
    if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
    else {
        int invn = qpow(n);
        for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
    }
}

void px(int *f, int *g, int n) {
    for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}

void covolution(int *f, int *g, int len, int lim) {
    static int sav[N << 1];
    int n; for (n = 1; n < len << 1; n <<= 1);
    clr(sav, n); cpy(sav, g, n);
    NTT(sav, 1, n); NTT(f, 1, n);
    px(f, sav, n); NTT(f, -1, n);
    clr(f + lim, n - lim), clr(sav, n);
}

void Poly_inv(int *f, int m) {
    static int g1[N << 1], g2[N << 1], sav[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    g1[0] = qpow(f[0]);
    for (int len = 2; len <= n; len <<= 1) {
        cpy(g2, g1, len >> 1), cpy(sav, f, len);
        NTT(g2, 1, len), NTT(sav, 1, len);
        px(g2, sav, len); NTT(g2, -1, len);
        clr(g2, len >> 1); cpy(sav, g1, len);
        NTT(sav, 1, len); NTT(g2, 1, len);
        px(g2, sav, len); NTT(g2, -1, len);
        for (int i = len >> 1; i < len; i ++ ) g1[i] = (2ll * g1[i] - g2[i] + mod) % mod;
    }
    cpy(f, g1, m), clr(g1, n), clr(g2, n), clr(sav, n);
}

int n, m, F[N], G[N];
int pow2[N], frac[N], inv[N];

void solve() {
    cin >> n;
    frac[0] = inv[0] = 1;
    for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod;
    inv[n] = qpow(frac[n]);
    for (int i = n - 1; i; i -- ) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    for (int i = 0; i <= n; i ++ ) pow2[i] = qpow(2, 1ll * i * (i - 1) / 2);
    for (int i = 0; i < n; i ++ ) F[i] = 1ll * pow2[i] * inv[i] % mod;
    for (int i = 1; i <= n; i ++ ) G[i] = 1ll * pow2[i] * inv[i - 1] % mod;
    Poly_inv(F, n); covolution(F, G, n + 1, n + 1);
    cout << 1ll * F[n] * frac[n - 1] % mod << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T -- ) solve();
    return 0;
}

有标号DAG计数

考虑设 \(f_i\)\(i\) 点带标号 DAG 的数量,我们考虑 钦定\(j\) 个点的 入度\(0\),那么意味着我们可以删掉这些点得到较少部分点的方案数递推。

考虑设 \(f_i\) 中恰好有 \(j\) 个点的入度为 \(0\) 的方案数为 \(g_{i, \, j}\),至少有 \(j\) 个点的入度为 \(0\) 的方案数为 \(f_{i, \, j}\),容易得出:

\[f_{i, \, j} = \sum_{k = j}^{i}\binom{k}{j}g_{i, \, k} \]

由二项式反演有:

\[g_{i, \, j} = \sum_{k = j}^{i}(-1)^{k - j}\binom{k}{j}f_{i, \, k} \]

\(f_i\) 可以表示为:

\[\begin{aligned}f_i &= \sum_{j = 0}^{i}g_{i, \, j} \\ &= \sum_{j = 0}^{i}\sum_{k = j}^{i}(-1)^{k - j}\binom{k}{j}f_{i, k}\end{aligned} \]

考虑前面 \(j\) 个度为 \(0\) 的点可以向后任意连边,即:

\[f_{i, \, j} = 2^{j(i - j)}\binom{i}{j}f_{i - j} \]

于是我们可以这样化简:

\[\begin{aligned}f_i &= \sum_{j = 1}^{i}\sum_{k = j}^{i}(-1)^{k - j}\binom{k}{j}f_{i, k} \\ &= \sum_{j = 1}^{i}\sum_{k = j}^{i}(-1)^{k - j}\binom{k}{j}2^{k(i - k)}\binom{i}{k}f_{i - k} \\ &= \sum_{k = 1}^{i}\sum_{j = 1}^{k}(-1)^{k - j}\binom{k}{j}2^{k(i - k)}\binom{i}{k}f_{i - k} \\ &= \sum_{k = 1}^{i}2^{k(i - k)}\binom{i}{k}f_{i - k}\sum_{j = 1}^{k}(-1)^{k - j}\binom{k}{j} \\ &= \sum_{k = 1}^{i}2^{k(i - k)}\binom{i}{k}f_{i - k}\left(\sum_{j = 0}^{k}(-1)^{k - j}\binom{k}{j} - (-1)^k\right) \\ &= \sum_{k = 1}^{i}(-1)^{k - 1}2^{k(i - k)}\binom{i}{k}f_{i - k} \\ &= \sum_{j = 1}^{i}(-1)^{j - 1}2^{j(i - j)}\binom{i}{j}f_{i - j}\end{aligned} \]

考虑 \(ij = \binom{i + j}{2} - \binom{i}{2} - \binom{j}{2}\),则:

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

\[\begin{aligned}f_i &= \sum_{j = 1}^{i}(-1)^{j - 1}\frac{i!}{j!(i - j)!}\frac{2^{\binom{i}{2}}}{2^{\binom{j}{2}}2^{\binom{i - j}{2}}}f_{i - j} \\ &= \sum_{j = 1}^{i}(-1)^{j - 1}\frac{i!}{j!(i - j)!}\frac{2^{\binom{i}{2}}}{2^{\binom{j}{2}}2^{\binom{i - j}{2}}}f_{i - j}\end{aligned} \]

于是,我们移项可以得到:

\[\frac{f_i}{i!2^{\binom{i}{2}}} = \sum_{j = 1}^{i}\frac{(-1)^{j - 1}}{j!2^{\binom{j}{2}}}\cdot\frac{f_{i - j}}{(i - j)!2^{\binom{i - j}{2}}} \]

\(F(x) = \sum\limits_{i = 1}^{n}\frac{(-1)^{i - 1}}{i!2^{\binom{i}{2}}}\)\(G(x) = \sum\limits_{i = 0}^{n}\frac{f_i}{i!2^{\binom{i}{2}}}\),那么容易看出:

\[G(x) = F(x)G(x) + 1 \pmod{x^{n + 1}} \]

也就是 \(G(x) = \frac{1}{1 - F(x)}\),使用多项式求逆可以得到答案。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define PPI pair<ll, PII>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int _G = 3, _i = 86583718, mod = 998244353;
const int N = 1.5e5 + 10, INF = 1e9;

ll qpow(ll a, ll k = mod - 2, ll p = mod) {
    ll res = 1;
    while (k) {
        if (k & 1) res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}

const int invG = qpow(_G), inv2 = qpow(2);
int rev[N << 1], rev_len;

void rev_init(int n) {
    if (rev_len == n) return;
    for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
    rev_len = n;
}

void NTT(int *g, int op, int n) {
    rev_init(n);
    static ull f[N << 1], Gk[N << 1] = {1};
    for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
    for (int k = 1; k < n; k <<= 1) {
        int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
        for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
        for (int i = 0; i < n; i += k << 1) {
            for (int j = 0; j < k; j ++ ) {
                int tmp = Gk[j] * f[i | j | k] % mod;
                f[i | j | k] = f[i | j] + mod - tmp;
                f[i | j] += tmp;
            }
        }
        if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
    }
    if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
    else {
        int invn = qpow(n);
        for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
    }
}

void px(int *f, int *g, int n) {
    for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}

void covolution(int *f, int *g, int len, int lim) {
    static int sav[N << 1];
    int n; for (n = 1; n < len << 1; n <<= 1);
    clr(sav, n); cpy(sav, g, n);
    NTT(sav, 1, n); NTT(f, 1, n);
    px(f, sav, n); NTT(f, -1, n);
    clr(f + lim, n - lim), clr(sav, n);
}

void Poly_inv(int *f, int m) {
    static int b1[N << 1], b2[N << 1], sav[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    b1[0] = qpow(f[0]);
    for (int len = 2; len <= n; len <<= 1) {
        cpy(b2, b1, len >> 1), cpy(sav, f, len);
        NTT(b2, 1, len), NTT(sav, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        clr(b2, len >> 1); cpy(sav, b1, len);
        NTT(sav, 1, len); NTT(b2, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        for (int i = len >> 1; i < len; i ++ ) b1[i] = (2ll * b1[i] - b2[i] + mod) % mod;
    }
    cpy(f, b1, m), clr(b1, n), clr(b2, n), clr(sav, n);
}

int n, F[N << 1], frac[N], pw2[N];

void solve() {
    cin >> n;
    F[0] = frac[0] = 1;
    for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod, pw2[i] = qpow(2, 1ll * i * (i - 1) >> 1, mod - 1);
    for (int i = 1; i <= n; i ++ ) {
        F[i] = pw2[i] * frac[i] % mod;
        if (i & 1) F[i] = (mod - F[i]) % mod;
        F[i] = qpow(F[i]);
    }
    Poly_inv(F, n + 1);
    for (int i = 1; i <= n; i ++ ) cout << 1ll * F[i] * pw2[i] % mod * frac[i] % mod << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T -- ) solve();
    return 0;
}

有标号弱连通 DAG 计数

这个题考虑了 DAG 是弱连通的,我们考虑这个 DAG 总共被分成了 \(k\) 个联通块,设 \(H(x)\) 为弱连通 DAG 的 EGF,\(G_k(x)\) 为 DAG 有 \(k\) 个联通块的 EGF,那么可以得到:

\[G_k(x) = \frac{1}{k!}H^{k}(x) \]

\[G(x) = \sum_{i = 0}^{n}G_i(x) = \sum_{i = 0}^{n}\frac{1}{i!}H^{i}(x) \]

考虑麦克劳林级数,可以得到:

\[e^{H(x)} = \sum\limits_{i = 0}^{n}\frac{1}{i!}H^{i}(x) \]

这告诉我们 \(G(x) = e^{H(x)}\),即 \(H(x) = \ln{G(x)}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define PPI pair<ll, PII>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int _G = 3, _i = 86583718, mod = 998244353;
const int N = 1.5e5 + 10, INF = 1e9;

ll qpow(ll a, ll k = mod - 2) {
    ll res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}

const int invG = qpow(_G), inv2 = qpow(2);
int rev[N << 1], rev_len;

void rev_init(int n) {
    if (rev_len == n) return;
    for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
    rev_len = n;
}

void NTT(int *g, int op, int n) {
    rev_init(n);
    static ull f[N << 1], Gk[N << 1] = {1};
    for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
    for (int k = 1; k < n; k <<= 1) {
        int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
        for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
        for (int i = 0; i < n; i += k << 1) {
            for (int j = 0; j < k; j ++ ) {
                int tmp = Gk[j] * f[i | j | k] % mod;
                f[i | j | k] = f[i | j] + mod - tmp;
                f[i | j] += tmp;
            }
        }
        if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
    }
    if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
    else {
        int invn = qpow(n);
        for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
    }
}

void px(int *f, int *g, int n) {
    for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}

void covolution(int *f, int *g, int len, int lim) {
    static int sav[N << 1];
    int n; for (n = 1; n < len << 1; n <<= 1);
    clr(sav, n); cpy(sav, g, n);
    NTT(sav, 1, n); NTT(f, 1, n);
    px(f, sav, n); NTT(f, -1, n);
    clr(f + lim, n - lim), clr(sav, n);
}

int inv[N << 1], inv_len;

void inv_init(int n) {
    if (n <= inv_len) return;
    if (!inv_len) inv[0] = inv[1] = 1, inv_len = 1;
    for (int i = inv_len + 1; i <= n; i ++ ) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
    inv_len = n;
}

void Poly_d(int *f, int n) {
    for (int i = 1; i < n; i ++ ) f[i - 1] = 1ll * f[i] * i % mod;
    f[n - 1] = 0;
}

void Poly_int(int *f, int n) {
    for (int i = n; i; i -- ) f[i] = 1ll * f[i - 1] * inv[i] % mod;
    f[0] = 0;
}

void Poly_inv(int *f, int m) {
    static int b1[N << 1], b2[N << 1], sav[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    b1[0] = qpow(f[0]);
    for (int len = 2; len <= n; len <<= 1) {
        cpy(b2, b1, len >> 1), cpy(sav, f, len);
        NTT(b2, 1, len), NTT(sav, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        clr(b2, len >> 1); cpy(sav, b1, len);
        NTT(sav, 1, len); NTT(b2, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        for (int i = len >> 1; i < len; i ++ ) b1[i] = (2ll * b1[i] - b2[i] + mod) % mod;
    }
    cpy(f, b1, m), clr(b1, n), clr(b2, n), clr(sav, n);
}

void Poly_ln(int *f, int n) {
    static int sav[N << 1];
    inv_init(n); cpy(sav, f, n);
    Poly_d(sav, n); Poly_inv(f, n);
    covolution(f, sav, n, n); Poly_int(f, n - 1);
    clr(sav, n);
}

int n, F[N << 1];
int frac[N], pw2[N];

void solve() {
    cin >> n;
    F[0] = frac[0] = 1;
    for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod, pw2[i] = qpow(2, 1ll * i * (i - 1) >> 1);
    for (int i = 1; i <= n; i ++ ) {
        F[i] = qpow(1ll * pw2[i] * frac[i] % mod);
        if (i & 1) F[i] = (mod - F[i]) % mod;
    }
    Poly_inv(F, n + 1);
    for (int i = 1; i <= n; i ++ ) F[i] = 1ll * F[i] * pw2[i] % mod;
    Poly_ln(F, n + 1);
    for (int i = 1; i <= n; i ++ ) cout << 1ll * F[i] * frac[i] % mod << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T -- ) solve();
    return 0;
}

还有一种不用 \(\exp\) 的方法,我们考虑我们已经算出了 DAG 的方案数,弱连通 DAG 的方案数相当于总的方案数减去不连通的方案数,设 \(g_i\) 为弱连通 DAG 的数量,\(f_i\) 为任意 DAG 的数量,我们考虑枚举 \(1\) 号点所在的联通块大小:

\[g_i = f_i - \sum_{j = 1}^{i - 1}\binom{i - 1}{i - j - 1}f_jg_{i - j} \]

那么我们可以移项得到:

\[\begin{aligned}f_i - g_i &= \sum_{j = 1}^{i - 1}\binom{i - 1}{i - j - 1}f_jg_{i - j} \\ &= \sum_{j = 1}^{i - 1}\frac{(i - 1)!}{(i - j - 1)!j!}f_jg_{i - j} \end{aligned}\]

\((i - 1)!\) 除过去可以得到:

\[\frac{f_i}{(i - 1)!} - \frac{g_i}{(i - 1)!} = \sum_{j = 1}^{i - 1}\frac{f_j}{j!}\cdot\frac{g_{i - j}}{(i - j - 1)!} \]

\(F(x) = \sum\limits_{i = 1}^{n}\frac{f_i}{i!}\)\(G(x) = \sum\limits_{i = 1}^{n}\frac{f_i}{(i - 1)!}\)\(H(x) = \sum\limits_{i = 1}^{n}\frac{g_i}{(i - 1)!}\),易知 \(G(x) - H(x) = F(x)H(x)\),则 \(H(x) = \frac{G(x)}{F(x) + 1}\),多项式求逆可以解决。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define PPI pair<ll, PII>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int _G = 3, _i = 86583718, mod = 998244353;
const int N = 1.5e5 + 10, INF = 1e9;

ll qpow(ll a, ll k = mod - 2) {
    ll res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}

const int invG = qpow(_G), inv2 = qpow(2);
int rev[N << 1], rev_len;

void rev_init(int n) {
    if (rev_len == n) return;
    for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
    rev_len = n;
}

void NTT(int *g, int op, int n) {
    rev_init(n);
    static ull f[N << 1], Gk[N << 1] = {1};
    for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
    for (int k = 1; k < n; k <<= 1) {
        int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
        for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
        for (int i = 0; i < n; i += k << 1) {
            for (int j = 0; j < k; j ++ ) {
                int tmp = Gk[j] * f[i | j | k] % mod;
                f[i | j | k] = f[i | j] + mod - tmp;
                f[i | j] += tmp;
            }
        }
        if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
    }
    if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
    else {
        int invn = qpow(n);
        for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
    }
}

void px(int *f, int *g, int n) {
    for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}

void covolution(int *f, int *g, int len, int lim) {
    static int sav[N << 1];
    int n; for (n = 1; n < len << 1; n <<= 1);
    clr(sav, n); cpy(sav, g, n);
    NTT(sav, 1, n); NTT(f, 1, n);
    px(f, sav, n); NTT(f, -1, n);
    clr(f + lim, n - lim), clr(sav, n);
}

int inv[N << 1], inv_len;

void inv_init(int n) {
    if (n <= inv_len) return;
    if (!inv_len) inv[0] = inv[1] = 1, inv_len = 1;
    for (int i = inv_len + 1; i <= n; i ++ ) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
    inv_len = n;
}

void Poly_d(int *f, int n) {
    for (int i = 1; i < n; i ++ ) f[i - 1] = 1ll * f[i] * i % mod;
    f[n - 1] = 0;
}

void Poly_int(int *f, int n) {
    for (int i = n; i; i -- ) f[i] = 1ll * f[i - 1] * inv[i] % mod;
    f[0] = 0;
}

void Poly_inv(int *f, int m) {
    static int b1[N << 1], b2[N << 1], sav[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    b1[0] = qpow(f[0]);
    for (int len = 2; len <= n; len <<= 1) {
        cpy(b2, b1, len >> 1), cpy(sav, f, len);
        NTT(b2, 1, len), NTT(sav, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        clr(b2, len >> 1); cpy(sav, b1, len);
        NTT(sav, 1, len); NTT(b2, 1, len);
        px(b2, sav, len); NTT(b2, -1, len);
        for (int i = len >> 1; i < len; i ++ ) b1[i] = (2ll * b1[i] - b2[i] + mod) % mod;
    }
    cpy(f, b1, m), clr(b1, n), clr(b2, n), clr(sav, n);
}

void Poly_ln(int *f, int n) {
    static int sav[N << 1];
    inv_init(n); cpy(sav, f, n);
    Poly_d(sav, n); Poly_inv(f, n);
    covolution(f, sav, n, n); Poly_int(f, n - 1);
    clr(sav, n);
}

int n, F[N << 1], G[N << 1];
int frac[N], pw2[N], inv_frac[N];

void solve() {
    cin >> n;
    F[0] = frac[0] = inv_frac[0] = 1;
    for (int i = 1; i <= n; i ++ ) frac[i] = 1ll * frac[i - 1] * i % mod, pw2[i] = qpow(2, 1ll * i * (i - 1) >> 1);
    inv_frac[n] = qpow(frac[n]);
    for (int i = n - 1; i; i -- ) inv_frac[i] = 1ll * inv_frac[i + 1] * (i + 1) % mod;
    for (int i = 1; i <= n; i ++ ) {
        F[i] = qpow(1ll * pw2[i] * frac[i] % mod);
        if (i & 1) F[i] = (mod - F[i]) % mod;
    }
    Poly_inv(F, n + 1);
    for (int i = 1; i <= n; i ++ ) F[i] = 1ll * F[i] * pw2[i] % mod;
    for (int i = 1; i <= n; i ++ ) G[i] = 1ll * F[i] * i % mod;
    Poly_inv(F, n + 1);
    covolution(F, G, n + 1, n + 1);
    for (int i = 1; i <= n; i ++ ) cout << 1ll * F[i] * frac[i - 1] % mod << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    // cin >> T;
    while (T -- ) solve();
    return 0;
}
posted @ 2025-06-05 15:45  YipChip  阅读(50)  评论(0)    收藏  举报