[poly计数] [集训队作业2013]城市规划

\(\texttt{link}\)

\(f_i\)\(i\) 个点的简单有标号无向连通图的数目, \(g_i = 2^{\binom i 2}\)\(i\) 个点的简单有标号无向图的数目,

枚举 \(1\) 号点所在联通块的大小,有: \(g_n = \sum\limits_{i=1}^n\dbinom{n-1}{i-1}f_i g_{n-i}\)

拆掉组合数,\(\dfrac {g_n} {(n-1)!} = \sum\limits_{i=1}^n\dfrac {f_i}{(i-1)!}\dfrac {g_{n-i}} {(n-i)!}\)

\(H(x) = \sum\limits_{i=1}^n\dfrac{g_i}{(i-1)!},F(x) = \sum\limits_{i=1}^n \dfrac {f_i}{(i-1)!},G(x) = \sum\limits_{i=0}^n\dfrac{g_i}{i!}\)

\(H(x) \equiv F(x)G(x) \pmod {x^{n+1}}\)

要求的是 \([x^n]F(x)\),则 \(F(x) \equiv H(x)G'(x)\pmod{x^{n+1}}\),答案即为 \((n-1)![x^n]F(x)\)

\(\texttt{Code:}\)

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
int read() {
    int x = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return f ? -x : x;
}
const int cmd = 1004535809;
int fpow(int a, ll b) {
    int res = 1;
    for (; b; b >>= 1, a = 1ll * a * a % cmd)
        if (b & 1) res = 1ll * res * a % cmd;
    return res;
}
int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}
int sub(int a, int b) {a -= b; return a < 0 ? a + cmd : a;}
const int N = (1 << 19) + 5;
namespace Poly {
    int rev[N], w[N], yg = 3, invyg = fpow(3, cmd - 2), inv[N];
    void ntt(int *f, int n, int tp) {
        for (int i = 1; i < n; i++) {
            rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
            if (i < rev[i]) swap(f[i], f[rev[i]]);
        }
        for (int len = 2; len <= n; len <<= 1) {
            int w0 = fpow(tp ? yg : invyg, (cmd - 1) / len); w[0] = 1;
            for (int i = 1; i < (len >> 1); i++) w[i] = 1ll * w[i - 1] * w0 % cmd;
            for (int s = 0; s < n; s += len)
            for (int i = 0; i < (len >> 1); i++) {
                int cur = 1ll * f[s + (len >> 1) + i] * w[i] % cmd;
                f[s + (len >> 1) + i] = sub(f[s + i], cur);
                f[s + i] = add(f[s + i], cur);
            }
        }
        if (!tp) {
            int invn = fpow(n, cmd - 2);
            for (int i = 0; i < n; i++) f[i] = 1ll * f[i] * invn % cmd;
        }
    }
    void tms(int *f, int *g, int n) {
        for (int i = 0; i < n; i++) f[i] = 1ll * f[i] * g[i] % cmd;
    }
    void invp(int *f, int m) {
        static int A[N], B[N], C[N];
        int n = 1; for (; n < m; n <<= 1);
        A[0] = fpow(f[0], cmd - 2);
        for (int len = 2; len <= n; len <<= 1) {
            for (int i = 0; i < (len >> 1); i++)
                B[i] = (A[i] << 1) % cmd;
            memcpy(C, f, sizeof(int)*len);
            ntt(A, len << 1, 1); tms(A, A, len << 1);
            ntt(C, len << 1, 1); tms(A, C, len << 1);
            ntt(A, len << 1, 0); memset(A + len, 0, sizeof(int)*len);
            for (int i = 0; i < len; i++)
                A[i] = sub(B[i], A[i]);
        }memcpy(f, A, sizeof(int)*m);
        memset(A, 0, sizeof(int)*n);
        memset(B, 0, sizeof(int)*n);
        memset(C, 0, sizeof(int)*(n << 1));
    }
    void NTT(int *f, int *g, int m) {
        int n = 1; for (; n <= m + m; n <<= 1);
        ntt(f, n, 1); ntt(g, n, 1);
        tms(f, g, n); ntt(f, n, 0);
    }
}using namespace Poly;
int n, g[N], h[N], fac[N], ifac[N];
int main() {
    #ifdef LOCAL
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    #endif
    n = read();
    fac[0] = ifac[0] = 1;
    for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % cmd;
    ifac[n] = fpow(fac[n], cmd - 2);
    for (int i = n - 1; i; i--) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % cmd;
    g[0] = g[1] = h[1] = 1;
    for (int i = 2; i <= n; i++) g[i] = 1ll * fpow(2, 1ll * i * (i - 1) / 2) * ifac[i] % cmd;
    for (int i = 2; i <= n; i++) h[i] = 1ll * fpow(2, 1ll * i * (i - 1) / 2) * ifac[i - 1] % cmd;
    invp(g, n + 1); NTT(g, h, n);
    g[n] = 1ll * g[n] * fac[n - 1] % cmd;
    printf("%d", g[n]);
    return 0;
}
posted @ 2021-12-20 19:35  klii  阅读(43)  评论(0)    收藏  举报