【THUPC 2018】好图计数
Problem#
Description#
这道题目非常简单,它甚至没有题目背景、没有任何故事。
但为了能让你顺利理解题目,善良的 Yazid 将为你介绍一些概念。
-
简单图:不存在重边、自环的图。(重边即为两条完全相同的边,自环即为两端点为同一节点的边)
-
补图:一个图 G 的补图有与 G 完全相同的节点,且任意两点之间有边当且仅当他们在 G 中不相邻。
我们归纳定义一个无向简单图是好的:
-
一个单点是好的。
-
若干个好的图分别作为联通块所形成的图是好的。
-
一个好的图的补图是好的。
总共 T 组数据。
每组数据给定一个正整数 n。
求 n 个点的本质不同的好的图的数量对质数 P 取模的结果。
两个好的图的被认为是本质不同的,当且仅当无论如何将一个图重标号,它都不能与另一个图完全相同。
Range#
T≤233,n≤23333,229<P<230 ,且 P 为质数。
Algorithm#
生成函数,DP 。
Mentality#
典型的利用生成函数寻找性质。
根据题目这个奇怪的定义,我们可以得到以下结论:
不难发现,当超过一个点的时候,一个联通图要成为好图,必须依靠条件 3 。
同时,对于一个不联通的图,它的补图一定是个联通图。证明很简单,对于任意两个联通块 A 和 B ,在补图里,A 中的每个点都会向 B 中的每个点连边,则两个联通块自然就联通了。
则我们的联通好图和不联通好图一定可以成对两两互补。
所以设 fn 为 n 个点的好图个数,gn 为 n 个点的联通好图个数,则有:fn=2gn 。
为了推式子比较方便,我们设 f0=1 。
不难发现,对于大小为 k 的联通好图,其能够组成的好图方案的生成函数为:
则我们可以列出 {fn} 的生成函数 F 的式子:
发现右边是 ∏ 特别不好搞,于是考虑用 ln 拆成加法,然后再求导去掉 ln。
接下来我们要推递推用的式子了:
对于 xk−11−xk 来说,考虑 11−xk=∑i>=0xik ,则有:
故可得:
然后我们设 hi=∑j|ij∗gj
代回原式便有:
发现由于在计算 f0∗h(n+1) 的时候式中包含未知的 gn+1=fn+12 ,所以将其移到左边去,则式子变为:
因为 O2 很猛,直接 n2 卡常递推就可以过了。
至于怎么卡常……什么 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\n", f[n]);
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 用 C# 插值字符串处理器写一个 sscanf
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!