Brief Description

给出标号为1到N的点,以及某些点最终的度数,允许在

任意两点间连线,可产生多少棵度数满足要求的树?

Algorithm Design

结论题.

首先可以参考这篇文章了解一下什么是Prufer编码:



Cayley公式是说,一个完全图\(K_n\)有\(n^{n-2}\)棵生成树,换句话说n个节点的带标号的无根树有\(n^{n-2}\)个。今天我学到了Cayley公式的一个非常简单的证明,证明依赖于Prüfer编码,它是对带标号无根树的一种编码方式。

给定一棵带标号的无根树,找出编号最小的叶子节点,写下与它相邻的节点的编号,然后删掉这个叶子节点。反复执行这个操作直到只剩两个节点为止。由于节点数n>2的树总存在叶子节点,因此一棵n个节点的无根树唯一地对应了一个长度为n-2的数列,数列中的每个数都在1到n的范围内。下面我们只需要说明,任何一个长为n-2、取值范围在1到n之间的数列都唯一地对应了一棵n个节点的无根树,这样我们的带标号无根树就和Prüfer编码之间形成一一对应的关系,Cayley公式便不证自明了。

注意到,如果一个节点A不是叶子节点,那么它至少有两条边;但在上述过程结束后,整个图只剩下一条边,因此节点A的至少一个相邻节点被去掉过,节点A的编号将会在这棵树对应的Prüfer编码中出现。反过来,在Prüfer编码中出现过的数字显然不可能是这棵树(初始时)的叶子。于是我们看到,没有在Prüfer编码中出现过的数字恰好就是这棵树(初始时)的叶子节点。找出没有出现过的数字中最小的那一个(比如④),它就是与Prüfer编码中第一个数所标识的节点(比如③)相邻的叶子。接下来,我们递归地考虑后面n-3位编码(别忘了编码总长是n-2):找出除④以外不在后n-3位编码中的最小的数(左图的例子中是⑦),将它连接到整个编码的第2个数所对应的节点上(例子中还是③)。再接下来,找出除④和⑦以外后n-4位编码中最小的不被包含的数,做同样的处理……依次把③⑧②⑤⑥与编码中第3、4、5、6、7位所表示的节点相连。最后,我们还有①和⑨没处理过,直接把它们俩连接起来就行了。由于没处理过的节点数总比剩下的编码长度大2,因此我们总能找到一个最小的没在剩余编码中出现的数,算法总能进行下去。这样,任何一个Prüfer编码都唯一地对应了一棵无根树,有多少个n-2位的Prüfer编码就有多少个带标号的无根树。

一个有趣的推广是,n个节点的度依次为D1, D2, …, Dn的无根树共有\(\frac{(n-2)!}{\prod{(D_i-1)!}}\)个,因为此时Prüfer编码中的数字i恰好出现Di-1次。

然后, 根据排列组合和计数原理的相关知识, 设没有限制的节点数为\(m\), 答案就是

然后在分子和分母上分解因式, 把次数加加减减就好了.

Code

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define ll long long
const int maxn = 3000;
const ll mod = 1000000000000000;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, m, tot, cnt;
ll d[maxn], num[maxn], prime[maxn], check[maxn], ans[maxn], l = 1;
void getprime() {
for (int i = 2; i <= 1000; i++) {
if (!check[i])
prime[cnt++] = i;
for (int j = 0; j < cnt; j++) {
if (i * prime[j] > 1000)
break;
check[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void solve(int a, int f) {
for (int k = 1; k <= a; k++) {
int x = k;
for (int i = 0; i < cnt; i++) {
if (x <= 1)
break;
while (x % prime[i] == 0) {
num[i] += f;
x /= prime[i];
}
}
}
}
void mul(int x) {
for (int i = 1; i <= l; i++)
ans[i] *= x;
for (int i = 1; i <= l; i++) {
ans[i + 1] += ans[i] / mod;
ans[i] %= mod;
}
while (ans[l + 1]) {
l++;
ans[l + 1] += ans[l] / mod;
ans[l] %= mod;
}
}
void print() {
for (int i = l; i; i--)
if (i == l)
printf("%lld", ans[i]);
else
printf("%06lld", ans[i]);
}
int main() {
getprime();
ans[1] = 1;
n = read();
if (n == 1) {
int x = read();
if (!x || x == -1)
printf("1");
else
printf("0");
return 0;
}
for (int i = 1; i <= n; i++) {
d[i] = read();
if (!d[i]) {
printf("0");
return 0;
}
if (d[i] == -1)
m++;
else {
d[i]--;
tot += d[i];
}
}
if (tot > n - 2) {
printf("0");
return 0;
}
solve(n - 2, 1);
solve(n - 2 - tot, -1);
for (int i = 1; i <= n; i++)
solve(d[i], -1);
for (int i = 0; i < cnt; i++)
while (num[i]--)
mul(prime[i]);
for (int i = 1; i <= n - 2 - tot; i++)
mul(m);
print();
return 0;
}

[bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度的更多相关文章

  1. [BZOJ1005] [HNOI2008] 明明的烦恼 (prufer编码)

    Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? Input 第一行为N ...

  2. BZOJ 1005 [HNOI2008]明明的烦恼 (Prufer编码 + 组合数学 + 高精度)

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5786  Solved: 2263[Submit][Stat ...

  3. bzoj 1005 [HNOI2008] 明明的烦恼 (prufer编码)

    [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5907  Solved: 2305[Submit][Status][Di ...

  4. 【bzoj1005】[HNOI2008]明明的烦恼 Prufer序列+高精度

    题目描述 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树? 输入 第一行为N(0 < N < = 1000),接下来N行,第i+1行给出第i ...

  5. bzoj1005: [HNOI2008]明明的烦恼 prufer序列

    https://www.lydsy.com/JudgeOnline/problem.php?id=1005 给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的 ...

  6. bzoj1005: [HNOI2008]明明的烦恼(prufer+高精度)

    1005: [HNOI2008]明明的烦恼 题目:传送门 题解: 毒瘤题啊天~ 其实思考的过程还是比较简单的... 首先当然还是要了解好prufer序列的基本性质啦 那么和1211大体一致,主要还是利 ...

  7. bzoj 1005: [HNOI2008]明明的烦恼 prufer编号&&生成树计数

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 2248  Solved: 898[Submit][Statu ...

  8. bzoj1005 [HNOI2008]明明的烦恼

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3032  Solved: 1209 Description ...

  9. BZOJ1005 HNOI2008明明的烦恼(prufer+高精度)

    每个点的度数=prufer序列中的出现次数+1,所以即每次选一些位置放上某个点,答案即一堆组合数相乘.记一下每个因子的贡献分解一下质因数高精度乘起来即可. #include<iostream&g ...

随机推荐

  1. LintCode-69.二叉树的层次遍历

    二叉树的层次遍历 给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问) 样例 给一棵二叉树 {3,9,20,#,#,15,7} : 返回他的分层遍历结果: [     [3],     [9,2 ...

  2. iOS-Masonry用法

    __weak typeof(self) weakSelf = self; UIView * tempView = [[UIView alloc]init]; NSInteger count = ;// ...

  3. DataSet和List 泛型之间互相转换 (转载)

    //DataSet与泛型集合间的互相转换 //利用反射机制将DataTable的字段与自定义类型的公开属性互相赋值. //注意:从DataSet到IList<T>的转换,自定义类型的公开属 ...

  4. Python ZKPython 安装

    1.由于python客户端依赖c的客户端所以要先安装c版本的客户端cd zookeeper-3.4.5/src/c./configuremake make install 2.下载python扩展包, ...

  5. VC学习笔记:对话框

    VC学习笔记:对话框 SkySeraph NOV.11st 2009 HQU Email-zgzhaobo@gmail.com  QQ-452728574 Latest Modified Date:O ...

  6. IO Model- 同步,异步,阻塞,非阻塞

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  7. HDU 4869 Turn the pokers(思维+逆元)

    考试的时候没有做出来... 想到了答案一定是一段连续的区间,一直在纠结BFS判断最后的可行1数. 原来直接模拟一遍就可以算出来最后的端点... 剩下的就是组合数取模了,用逆元就行了... # incl ...

  8. Luogu2540 斗地主增强版(搜索+动态规划)

    单纯的暴搜似乎还是很好写的,然而过不了.出完顺子之后答案是可以dp出来的,于是大力搜然后大力dp就好了. dp时强行讨论完了几乎所有拆牌情况,理性愉悦一发. #include<iostream& ...

  9. linq的decimal类型保存到数据库只保存到小数点后两位的问题

    今天的一个decimal类型保存到数据的问题困扰了我很长时间,最后就是一个小小的设置问题解决······坑······深坑···· 话不多说,直接说问题,在说答案: 问题:linq当采用EF的DbCo ...

  10. [洛谷P1440]求m区间内的最小值

    题目大意:给你n个数,求出每个数前m位的最小值 题解:单调队列,用一个可以双向弹出的队列来存一串数,满足里面的数具有单调性,我们可以假设它是单调递增的,即求最小的数.那么可以把要插入的这个数与队尾元素 ...