[THUPC2018]好图计数

题意

我们归纳定义一个无向简单图是好的

  1. 一个单点是好的。
  2. 若干个好的图分别作为联通块所形成的图是好的。
  3. 一个好的图的补图是好的。

给定一个正整数 \(n\)

\(n\) 个点的本质不同的好的图的数量对质数 \(P\) 取模的结果。

两个好的图的被认为是本质不同的,当且仅当无论如何将一个图重标号,它都不能与另一个图完全相同。

数据范围: $ T \leq 233, n \leq 23333$

题解

先推导一些关于好图的性质:

  • 除了单个点之外,原图及其补图都是连通图的图不是好图。

这个结论非常显然。

  • 一张图和它的补图不会同时为非连通图。

考虑一张非连通图的点 \((u, v)\), 如果原先不连通,则现在有边;如果原先连通,则存在一个点 \(k\) 在原图中和 \(u, v\) 都没有边,那么补图中边 \((u, k)\) 和 $ (v, k)$ 就存在。

这两个十分显然的结论就会推出一个极其有用的结论:

  • 除了单个点之外,连通好图和非连通好图成对出现。

\(f_n\)\(n\) 个点好图个数, \(n \not= 1\) 时, \(g_n\) 为连通好图个数,也为非连通好图个数。

连通好图可以划分为若干个非连通好图:
\[ g_n = \prod_{i = 1} ^ {n - 1} (\sum_{j \geq 0} \binom{j + g_i - 1}{g_i - 1} x^{ij} ) [x^n] \]
从而就可以得到 \(f\)\(OGF\)
\[ \begin{split} F &=\prod_{i\geq1}(\sum_{j\geq0} \binom{j + g_i - 1}{g_i - 1} x ^ {ij}) \\ &= \prod_{i\geq1} (1 - x^i) ^{ -g_i} \end{split} \]
两边取 \(ln\):
\[ ln F = - \sum_{i \geq 1} g_i ln(1 - x^i) \]
两边求导:
\[ \frac{F'}{F} = \sum_{i \geq 1} g_i \frac{i x ^ {i - 1}}{1 - x^i} \]
这就得到:
\[ F' = F \sum_{i \geq 1} g_i \frac{i x ^ {i - 1}}{1 - x^i} \\(n+1)f_{n+1} = \sum_{i = 0} ^ n f_{n-i} (\sum_{j \geq 1} g_j \frac{j x ^ {j - 1}}{1 - x^j} [x^{i}]) \]
因为有:
\[ \frac{x^{j - 1}}{1 - x ^ j} = \sum_{i \leq 1} x^{ij-1} \]
因此\(\frac{x^{j - 1}}{1 - x ^ j} [x_i] = [i | (j + 1)]\), 得到:
\[ (n + 1) f_{n +1} = \sum_{i = 0} ^ n f_i \sum_{j | n + 1 - i} j g_j \]

特判 $ i = 0$ 和 $ n = 1$,然后 \(O(n^2)\) 递推即可。

用 __int128 优化掉取模之后跑得飞快。

#pragma GCC optimize("2,Ofast,inline")
#include<bits/stdc++.h>
using namespace std;
const int N = 23334;

int n, m, mod;
int f[N], g[N], h[N];

const int Maxn = 23334;
    
int fac[Maxn], fav[Maxn], inv[Maxn];

void comb_init() {
    fac[0] = fav[0] = 1;
    inv[1] = fac[1] = fav[1] = 1;
    for (int i = 2; i < Maxn; ++i) {
        fac[i] = 1LL * fac[i - 1] * i % mod;
        inv[i] = 1LL * -mod / i * inv[mod % i] % mod + mod;
        fav[i] = 1LL * fav[i - 1] * inv[i] % mod;
    }
}

inline void upd(int &x, int y) {
    (x += y) >= mod ? x -= mod : 0;
}

inline int add(int x, int y) {
    return (x += y) >= mod ? x - mod : x;
}

inline int dec(int x, int y) {
    return (x -= y) < 0 ? x + mod : x;
}

void doit(int x) {
    int del = 1LL * f[x] * x % mod;
    for (int i = x; i < N; i += x) {
        upd(h[i], del);
    }
}

void solve() {
    g[0] = 1;
    g[1] = f[1] = 1;
    doit(1);
    for (int i = 2; i < N; ++i) {
        __int128 tmp = 0;
        for (int j = 1; j < i; ++j) {
            tmp += (long long) g[j] * h[i - j];
        }
        for (int j = 1; j * j <= i; ++j) {
            if (i % j == 0) {
                tmp += (long long) j * f[j];
                if (j * j != i) {
                    tmp += (long long) (i / j) * f[i / j];
                }
            }
        }
        g[i] = tmp % mod;
        g[i] = 2LL * g[i] * inv[i] % mod;
        f[i] = 1LL * g[i] * inv[2] % mod;
        doit(i);
    }
}

int main() {
    int T;
    cin >> T >> mod;
    comb_init();
    solve();
    while (T--) {
        scanf("%d", &n);
        printf("%d\n", g[n] % mod);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Vexoben/p/11786119.html