就是求 n 个点 m 条边的带标号无向连通图个数。
首先可以用最暴力的 O(n6) 做法,直接按城市规划一题的容斥 DP 做法,
记 fn,m 表示答案,可以枚举 1 号点所在块的情况容斥计算。
O(n4) 做法是一个有意思的斯特林反演。
考虑一个选了 m 条边的方案,且形成 k 个连通块方案块的方案是 Fm,k。
现在我们还是 m 条边,但人为划分出来 j 个块一定不两两相连,但块内任意连边,方案是 Gm,j。
有组合关系:
Gm,k=∑j≥kS2(j,k)Fm,j
反演以后即:
Fm,k=∑j≥kS1(j,k)(−1)j−kGm,j
我们要求的即:
Fm,1=∑j≥1(j−1)!(−1)j−1Gm,j
考虑 DP 后面那个 G,暴力大概不止 O(n6) 。
但是我们发现算这个 G 只需要知道块的划分情况,那么这 m 条边的可以随意选取。
记 Hi,j,m 表示 DP 了 i 个点,分了 j 个块,当前共有 m 个边可以用的方案数。
则有:
Gm,j=∑k≥mHn,j,k(km)
直接 DP 这个 H 复杂度过高,接下来尝试缩去中间 j 那一维。
现在考虑把 (j−1)!(−1)j−1 的附加贡献在 DP 过程中算上,
发现我们可以先正常选一个块,并固定这个块的一号在最前面,
然后剩下的每加入一个块,带一个 −1 的系数并且把这个块的点任意插进去(只需保证不在开头,系数 (i+k−1k))进行 DP。
这样就做到让一个 k 个块的方案会算重 (k−1)! 次。
时间复杂度 O(n4)。
然而代码短小精悍。
void DP()
{
for(int i = 1
H[i][c2(i)] = 1;
for(int i = 1
for(int j = c2(i)
if(H[i][j])
for(int k = 1
H[i + k][j + c2(k)] = (H[i + k][j + c2(k)] - 1ll * H[i][j] * C[i + k - 1][k]) % mod;
for(int j = 0
{
for(int k = j; k <= c2(n)
F[j] = (F[j] + 1ll * H[n][k] * C[k][j]) % mod;
F[j] = (F[j] + mod) % mod;
printf("%d\n", F[j]);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】