【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\geq 1}(\sum_{j\geq 0} \binom{j + g_i - 1}{g_i - 1} x ^ {ij}) \\ &= \prod_{i\geq 1} (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;
}
posted @ 2019-11-03 14:22  Vexoben  阅读(208)  评论(0编辑  收藏  举报