洛谷 P5448 - [THUPC2018]好图计数(生成函数)
5448544854485448~
好久没做图计数了,开了一道就是不会做的题(其实是看到无标号就觉得要毒瘤东西就没想多久)其实这题对于“无标号”的处理方法,和 CF848D,唯一的相同点就是没有不同点(大雾
首先先考虑一个套路化处理图计数的方法:将图分为连通图和非连通图处理。应用到此题而言,有:对于一张点数 \(\ge 2\) 的连通好图,其补图必然是非连通的,因为一个连通块的好图必然要么是一个单点(点数 \(<2\)),要么是由某个好图取反得到的,不可能跟其他连通好图拼起来(因为只有一个连通块),而这个取反的好图必然是非连通的(否则它肯定还是只能通过取反得到,这样就成环了)。而对于非连通好图,其补图必然是连通好图,这个不多做解释,这样我们就构造了连通好图和非连通好图的双射。于是如果设 \(f_n\) 表示 \(n\) 个点好图个数,\(g_n\) 表示 \(n\) 个点连通好图个数,那么有 \(f_n=2g_n(n\ge 2)\)。
那么另一个等式又该怎么列呢?由于无标号不能直接无脑 \(\ln\),我们必须得另寻其道。考虑这个我脑瘫了没想到的套路:我们枚举一个连通块的点数 \(k\),再枚举其中每一个含有 \(k\) 个点的连通好图,然后我们考虑用了多少个这样的连通块,由于无标号,所以此处应用 OGF 而不是 EGF,写成生成函数的形式就是 \(F(x)=\prod\limits_{k\ge 1}(\sum\limits_{i\ge 0}x^{ik})^{g_k}\),其中 \(f\) 为 \(F\) 的 OGF。简单变换一下可得 \(F(x)=\prod\limits_{k\ge 1}(\dfrac{1}{1-x^k})^{g_k}\)。
我们看这个连乘非常不爽,套路化 \(\ln\)。
到这里你可能会拆 \(\ln(1-x^k)\),但正解并不是这么处理的,而是将等式两边同时求导,得
其中 \(H(x)=\sum\limits_{k\ge 1}kg_k\dfrac{x^{k-1}}{1-x^k}\)
注意到 \([x^n]\dfrac{x^{k-1}}{1-x^k}=[n\equiv k-1\pmod{k}]\),于是有
注意到右边 \(\sum\) 拆出来会有个 \((n+1)g_{n+1}\),减掉可以有
递推即可,推出来一个 \(g_k\) 就暴力更新 \(h_n=\sum\limits_{k\mid n}kg_k\)。
时间复杂度 \(n^2\),常数那么小且 \(n\) 只有 \(2\times 10^4\),你瞧不起谁呢?半在线卷积,狗都不写(
typedef __int128_t i128;
const int MAXN = 23333;
int mod, f[MAXN + 5], g[MAXN + 5], sum[MAXN + 5];
int qpow(int x, int e) {
int ret = 1;
for (; e; e >>= 1, x = 1ll * x * x % mod)
if (e & 1) ret = 1ll * ret * x % mod;
return ret;
}
void init(int n) {
f[0] = f[1] = g[1] = g[2] = 1; f[2] = 2;
for (int i = 1; i <= n; i++) sum[i]++;
for (int i = 2; i <= n; i += 2) sum[i] += 2;
for (int i = 3; i <= n; i++) {
i128 s = 0;
for (int j = 0; j < i; j++) s += 1ll * f[j] * sum[i - j];
g[i] = s % mod * qpow(i, mod - 2) % mod;
f[i] = 2 * g[i] % mod;
for (int j = i; j <= n; j += i) sum[j] = (sum[j] + s) % mod;
}
}
int main() {
int qu; scanf("%d%d", &qu, &mod); init(MAXN);
while (qu--) {int n; scanf("%d", &n); printf("%d\n", f[n]);}
return 0;
}