Problem
Description
这道题目非常简单,它甚至没有题目背景、没有任何故事。
但为了能让你顺利理解题目,善良的 Yazid 将为你介绍一些概念。
-
简单图:不存在重边、自环的图。(重边即为两条完全相同的边,自环即为两端点为同一节点的边)
-
补图:一个图 (G) 的补图有与 (G) 完全相同的节点,且任意两点之间有边当且仅当他们在 (G) 中不相邻。
我们归纳定义一个无向简单图是好的:
-
一个单点是好的。
-
若干个好的图分别作为联通块所形成的图是好的。
-
一个好的图的补图是好的。
总共 (T) 组数据。
每组数据给定一个正整数 (n)。
求 (n) 个点的本质不同的好的图的数量对质数 (P) 取模的结果。
两个好的图的被认为是本质不同的,当且仅当无论如何将一个图重标号,它都不能与另一个图完全相同。
Range
(Tle 233, nle 23333, 2^{29}< P< 2^{30}) ,且 (P) 为质数。
Algorithm
生成函数,(DP) 。
Mentality
典型的利用生成函数寻找性质。
根据题目这个奇怪的定义,我们可以得到以下结论:
不难发现,当超过一个点的时候,一个联通图要成为好图,必须依靠条件 (3) 。
同时,对于一个不联通的图,它的补图一定是个联通图。证明很简单,对于任意两个联通块 (A) 和 (B) ,在补图里,(A) 中的每个点都会向 (B) 中的每个点连边,则两个联通块自然就联通了。
则我们的联通好图和不联通好图一定可以成对两两互补。
所以设 (f_n) 为 (n) 个点的好图个数,(g_n) 为 (n) 个点的联通好图个数,则有:(f_n=2g_n) 。
为了推式子比较方便,我们设 (f_0=1) 。
不难发现,对于大小为 (k) 的联通好图,其能够组成的好图方案的生成函数为:
则我们可以列出 ({f_n}) 的生成函数 (F) 的式子:
发现右边是 (prod) 特别不好搞,于是考虑用 (ln) 拆成加法,然后再求导去掉 (ln)。
接下来我们要推递推用的式子了:
对于 (frac{x^{k-1}}{1-x^k}) 来说,考虑 (frac{1}{1-x^k}=sum_{i>=0}x^{ik}) ,则有:
故可得:
然后我们设 (h_i=sum_{j|i} j*g_j)
代回原式便有:
发现由于在计算 (f_0*h(n+1)) 的时候式中包含未知的 (g_{n+1}=frac{f_{n+1}}{2}) ,所以将其移到左边去,则式子变为:
因为 (O2) 很猛,直接 (n^2) 卡常递推就可以过了。
至于怎么卡常……什么 (FastMod) 加速取模都是假的,真正快到极致的就是不取模,用 (int128) 省去大量取模,你值得拥有。
Code
#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long
#define go(G, x, i, v)
for (int i = G.hd[x], v = G.to[i]; i; v = G.to[i = G.nx[i]])
#define inline __inline__ __attribute__((always_inline))
inline LL read() {
LL x = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') w = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * w;
}
const int Max_n = 3e4 + 5;
int T, mod, n;
int f[Max_n], g[Max_n], h[Max_n];
namespace Init {
int ksm(int a, int b = mod - 2) {
int res = 1;
for (; b; b >>= 1, a = (LL)a * a % mod)
if (b & 1) res = (LL) res * a % mod;
return res;
}
void main() {
n = 23333;
for (int i = 1; i <= n; i++) {
__int128 t = 0;
for (int j = 1; j < i; j++) t += (LL)f[j] * h[i - j];
f[i] = 2ll * (f[i] + t % mod) * ksm(i) % mod;
g[i] = (LL)ksm(2) * f[i] % mod + (i == 1), f[i] += (i == 1);
for (int j = i; j <= n; j += i) {
(h[j] += (LL)i * g[i] % mod) %= mod;
if (j > i) (f[j] += (LL)i * g[i] % mod) %= mod;
}
}
}
} // namespace Init
int main() {
#ifndef ONLINE_JUDGE
freopen("6389.in", "r", stdin);
freopen("6389.out", "w", stdout);
#endif
T = read(), mod = read();
Init::main();
while(T--) {
n = read();
printf("%d
", f[n]);
}
}