@description@

求有多少 n 点 n 边的无向连通图,满足第 i 个点的度数为 \(d_i\)。

原题传送门。

@solution@

如果是树显然就 prufer 定理;基环树就考虑拓展一下。

枚举环上的点集合 S,则环内部能够连出的方案数为 \(\frac{(|S| - 1)!}{2}\)(注意环的大小 \(|S| \geq 3\))。

接着,将环缩成一个新点 x',则它的度数为 \(d_{x'} = \sum_{i\in S}(d_i - 2)\)(注意 \(d_i \geq 2\))。

对缩点后的新图运用 prufer 定理,得到:

\[\frac{(n - |S| - 1)!}{(\sum_{i\in S}(d_i - 2) - 1)!\times \prod_{i\not \in S}(d_i - 1)!}
\]

不过 x' 的出边并不是全部相同。对于 x' 还有个系数 \(\frac{(\sum_{i\in S}(d_i - 2))!}{\prod_{i\in S}(d_i - 2)!}\),表示邻接边的分配方案。

因此整合得到:

\[\begin{aligned}
&\frac{(|S| - 1)!}{2}\times \frac{(n - |S| - 1)!}{(\sum_{i\in S}(d_i - 2) - 1)!\times \prod_{i\not \in S}(d_i - 1)!}\times \frac{(\sum_{i\in S}(d_i - 2))!}{\prod_{i\in S}(d_i - 2)!}\\
=&\frac{(|S| - 1)!}{2}\times \frac{(n - |S| - 1)!}{\prod_{i}(d_i - 1)!}\times (\sum_{i\in S}(d_i - 2)) \times \prod_{i\in S}(d_i - 1)
\end{aligned}
\]

把 \((|S|, \sum(d_i - 2))\) 当作状态做一个 O(n^3) 的 dp 求 \(\prod_{i\in S}(d_i - 1)\) 即可。

特判 n 个点连成一个大环的情况。

@accpeted code@

#include <cstdio>
#include <algorithm>
using namespace std; const int MAXN = 300;
const int MOD = int(1E9) + 7;
const int INV2 = (MOD + 1) / 2; inline int add(int x, int y) {return (x + y >= MOD ? x + y - MOD : x + y);}
inline int sub(int x, int y) {return (x - y < 0 ? x - y + MOD : x - y);}
inline int mul(int x, int y) {return 1LL * x * y % MOD;} int pow_mod(int b, int p) {
int ret = 1;
for(int i=p;i;i>>=1,b=mul(b,b))
if( i & 1 ) ret = mul(ret, b);
return ret;
} int fct[2*MAXN + 5], ifct[2*MAXN + 5];
void init() {
fct[0] = 1; for(int i=1;i<=2*MAXN;i++) fct[i] = mul(fct[i - 1], i);
for(int i=0;i<=2*MAXN;i++) ifct[i] = pow_mod(fct[i], MOD - 2);
} int dp[2][MAXN + 5][MAXN + 5]; int d[MAXN + 5], N;
int main() {
init(), scanf("%d", &N);
for(int i=1;i<=N;i++)
scanf("%d", &d[i]), d[i]--;
int tot = 0; dp[0][0][0] = 1;
for(int i=1;i<=N;i++) {
if( d[i] ) {
for(int j=0;j<i;j++)
for(int k=0;k<=tot;k++)
dp[1][j][k] = dp[0][j][k];
for(int j=0;j<i;j++)
for(int k=0;k<=tot;k++)
dp[0][j+1][k+d[i]-1] = add(dp[0][j+1][k+d[i]-1], mul(d[i], dp[1][j][k]));
tot += d[i] - 1;
}
}
int ans = mul(mul(fct[N-1], INV2), dp[0][N][0]), tmp = 1;
for(int i=1;i<=N;i++) tmp = mul(tmp, ifct[d[i]]);
for(int i=3;i<N;i++)
for(int j=1;j<=tot;j++) {
int coef = mul(mul(fct[i-1], INV2), mul(fct[N-i-1], tmp));
ans = add(ans, mul(mul(coef, j), dp[0][i][j]));
}
printf("%d\n", ans);
}

@details@

如果是不带度数限制基环树,还是用的 prufer 序列,和上述方法相同(貌似矩阵树解不了的样子。。。)

本题还有一个更快的 O(n^2) 做法:

\[\begin{aligned}
&\frac{(|S| - 1)!}{2}\times \frac{(n - |S| - 1)!}{(\sum_{i\in S}(d_i - 2) - 1)!\times \prod_{i\not \in S}(d_i - 1)!}\times \frac{(\sum_{i\in S}(d_i - 2))!}{\prod_{i\in S}(d_i - 2)!}\\
=&\frac{(|S| - 1)!\times (n - |S| - 1)!}{2}\times \frac{\sum_{i\in S}(d_i - 2)}{\prod_{i\not \in S}(d_i - 1)!\prod_{i \in S}(d_i - 2)!}\\
=&\frac{(|S| - 1)!\times (n - |S| - 1)!}{2}\times \sum_{i\in S}\frac{1}{\prod_{j\not \in S}(d_j - 1)!\times \prod_{j \in S, j\not = i}(d_j - 2)!\times (d_i - 3)}
\end{aligned}
\]

只需要存状态 \(|S|\) 以及是否贡献了 \(\frac{1}{d_i - 3}\)。一样需要特判。

最新文章

  1. 消息中间件MetaQ高性能原因分析-转自阿里中间件
  2. 【转】WIN32 控制台程序
  3. Neuroaesthetics神经美学
  4. python 实现二分法查找
  5. 查看mysql的版本
  6. SendMessage函数的常用消息及其应用大全
  7. 关于DIV+CSS和XHTML+CSS的理解
  8. 【DDD-Apwork框架】事件总线和事件聚合器
  9. c3p0写连接池 Demo
  10. css布局学习笔记之position属性
  11. linux下C++对线程的封装
  12. aix knowlgdgecenter
  13. SDK无法更新
  14. 关于PC端与手机端随着手指移动图片位置放生变化的拖拽事件
  15. MonkeyRunner 实现自动点击截屏后与本地图库进行对比输出
  16. Binary Watch
  17. Python的字典
  18. Spring拦截器总结
  19. python学习第四讲,python基础语法之判断语句,循环语句
  20. 利用Openssh后门 劫持root密码

热门文章

  1. Docker搭建VS Code Server ,设置访问密码随时随地写代码
  2. spring cloud系列教程第六篇-Eureka集群版
  3. 赛艇表演 51nod提高组模拟试题
  4. 应用4:利用Filter限制用户浏览权限
  5. 关于String是值传递还是引用传递
  6. thymeleaf怎么在页面上面格式化时间
  7. Alpha冲刺 —— 5.5
  8. Rocket - diplomacy - LazyModule的实例化(补)
  9. Java实现 LeetCode 316 去除重复字母
  10. Java实现BFS广度优先查找