题面:

一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,你的程序需要输出满足d(vi)=di的树的个数。

题解:

乍一看是组合数学,,,当然了,实际上也是组合数。

只不过要是知道prufer数列就很简单了。

那先来看看prufer数列吧!

将树转化成Prufer数列的方法
一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列中,重复以上步骤直到原图仅剩2个顶点。  ------------------------------摘自百度百科

同时,prufer数列还支持再转化为树:

设{a1,a2,..an-2}为一棵有n个节点的树的Prufer序列,另建一个集合G含有元素{1..n},找出集合中最小的未在Prufer序列中出现过的数,将该点与Prufer序列中首项连一条边,并将该点和Prufer序列首项删除,重复操作n-2次,将集合中剩余的两个点之间连边即可。  ------------------------------摘自百度百科

这里我们可以观察到,树和prufer数列是一一对应的关系,也就是说一棵树的prufer数列是唯一确定的,同理,一个prufer数列对应的树也是唯一确定的,

这就非常妙妙了。

我们通过观察将树转化为prufer数列的方法可以得知,一个点在prufer数列中出现的次数实际上就是一个点的入度-1,

也就是说这道题实际上是:

给定prufer数列的限制条件,求树的种数

然后我们又知道树和prufer数列是一一对应的,所以题面就变成了:

给定prufer数列中各个点的出现次数,求prufer数列不重复的全排列

那做法就很清晰了

因为prufer数列的长度是n-2,(看上面的转换方法很容易发现)

所以全排列是(n-2)!,但是由于有重复出现的点,因此这些排列中会有很多重复的,

有哪些重复呢?

稍微了解一点组合数相关知识就知道了,

假设点i出现了x次,那么对于任意一次排列,这x个相同的点都有x!种排列方式,

因此一种排列就会因为点i而被多计算x!次,其他也是一样的,

所以总的公式就是

(n-2)!/(s[1]! * s[2]!.....)

其中s[i]表示点i在prufer数列中的出现次数(也就是入度-1)

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 170
#define LL long long
int n,all;
int s[AC];
LL ans;
/*prufer序列emmmm,,,
貌似说的还是比较有道理的?
任意prufer序列可以对应唯一确定的树,所以可以求prufer序列的不重复的全排列
通过求prufer序列的过程可以感知到,,,一个点在prufer序列中的出现次数,应当是其度数-1
因此这里给出了度数,也就相当于给出了prufer序列,那么对其求不重复的全排列即可 一棵n个节点的无根树唯一地对应了一个长度为n-2的数列,数列中的每个数都在1到n的范围内。
上面这句话比较重要。通过上面的定理,
1)我们可以直接推出n个点的无向完全图的生成树的计数:n^(n-2) 即n个点的有标号无根树的计数。
2)一个有趣的推广是,n个节点的度依次为D1, D2, …, Dn的无根树共有 (n-2)! / [ (D1-1)!(D2-1)!..(Dn-1)! ] 个,
因为此时Prüfer编码中的数字i恰好出现Di-1次。
即 n种元素,共n-2个,其中第i种元素有Di-1个,求排列数。
3)n个节点的度依次为D1, D2, …, Dn,令有m个节点度数未知,求有多少种生成树?(BZOJ1005 明明的烦恼)
令每个已知度数的节点的度数为di,有n个节点,m个节点未知度数,left=(n-2)-(d1-1)-(d2-1)-...-(dk-1)
已知度数的节点可能的组合方式如下
(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left!
剩余left个位置由未知度数的节点随意填补,方案数为m^left
于是最后有
ans=(n-2)!/(d1-1)!/(d2-1)!/.../(dk-1)!/left! * m^left
by https://www.cnblogs.com/dirge/p/5503289.html */
int read()
{
int x=;char c=getchar();
while(c > '' || c < '') c=getchar();
while(c >= '' && c <= '') x=x* + c - '',c=getchar();
return x;
} void pre()
{
n=read();
for(R i=;i<=n;i++)
{
s[i]=read() - , all+=s[i] + ;
if(s[i] < && n != )//error!!! 如果只有一个节点的话是可以允许没有边的
{//但也要注意将边都集中在某几个点上以至于后面的点不合法的情况
printf("0\n");
exit();
}
}
if(all != * n - )//如果是不合法数据就直接退了
{
printf("0\n");
exit();
}
} void work()
{
ans=;
for(R i=n-; i> ;i--)//也许从高处开始乘会减少爆的可能性?毕竟除也是从高处开始的
{
ans *= i;
for(R j=;j<=n;j++)//防爆措施
{
if(s[j] <= ) continue;
while(!(ans % s[j]))
{
ans/=s[j],--s[j];
if(s[j] == ) break;
}
}
}
printf("%lld\n",ans);
} int main()
{
// freopen("in.in","r",stdin);
pre();
work();
// fclose(stdin);
return ;
}

[HNOI2004]树的计数 prufer数列的更多相关文章

  1. BZOJ 1211[HNOI2004]树的计数 - prufer数列

    描述 一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵.给定n,d1, d2, …, dn,编程需要输出满足d(vi) ...

  2. bzoj1211: [HNOI2004]树的计数 prufer编码

    题目链接 bzoj1211: [HNOI2004]树的计数 题解 prufer序 可重排列计数 代码 #include<bits/stdc++.h> using namespace std ...

  3. Luogu P2290 [HNOI2004]树的计数 Prufer序列+组合数

    最近碰了$prufer$ 序列和组合数..于是老师留了一道题:P2624 [HNOI2008]明明的烦恼 qwq要用高精... 于是我们有了弱化版:P2290 [HNOI2004]树的计数(考一样的可 ...

  4. BZOJ 1211 HNOI2004 树的计数 Prufer序列

    题目大意:给定一棵树中全部点的度数,求有多少种可能的树 Prufer序列.详细參考[HNOI2008]明明的烦恼 直接乘会爆long long,所以先把每一个数分解质因数.把质因数的次数相加相减.然后 ...

  5. 【BZOJ1005/1211】[HNOI2008]明明的烦恼/[HNOI2004]树的计数 Prufer序列+高精度

    [BZOJ1005][HNOI2008]明明的烦恼 Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间连线,可 ...

  6. bzoj1211: [HNOI2004]树的计数 prufer序列裸题

    一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵.给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di ...

  7. BZOJ1211: [HNOI2004]树的计数(prufer序列)

    Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2987  Solved: 1111[Submit][Status][Discuss] Descript ...

  8. [HNOI2004] 树的计数 - prufer序列

    给定树每个节点的 degree,问满足条件的树的数目. \(n\leq 150, ans \leq 10^{17}\) Solution 注意特判各种坑点 \(\sum d_i - 1 = n-2\) ...

  9. P2290 [HNOI2004]树的计数

    P2290 [HNOI2004]树的计数prufer序列模板题 #include <iostream> #include <cstdio> #include <queue ...

随机推荐

  1. AirtestIDE实践二:Poco框架试用

    上一篇用airtest框架做了一个梦幻西游手游的DEMO,这次看看poco的强大之处.首先安装poco:pip install pocoui 其次,把SDK集成到你家游戏中,我这直接用官网提供的一个U ...

  2. adb获取设备的序列号

    用数据线连接手机, 打开开发者模式, 并赋予相关权限, 在CMD命令行输入: adb devices 第一个参数即为设备的序列号, 第二个参数device表示设备的状态是在线.

  3. Selenium(Python)驱动Firefox浏览器

    我的版本是Firefox Setup 52.7.0.exe+geckodriver-v0.15.0-win64.zip, 把驱动geckodriver.exe放到Python安装目录下, 也可以指定驱 ...

  4. EditorGUI控件输入监听

    EditorGUI控件输入监听 在做编辑器开放的过程中,有时候要对用户输入进行判断和限制,但EditorGUI控件却没有触发回调,而是提供了一种麻烦的办法--使用EditorGUI.BeginChan ...

  5. Swift使用AVAudioPlayer来调节游戏的背景音乐大小

    音乐文件的声音大小有时在做为游戏背景音乐时会过大,而如果我们只是简单应用SKAudioNode来加载音乐的话,是无法进行声音大小的调节的,因此我们必须使用更强大的AVAudioPlayer来进行声音大 ...

  6. JVM学习--jvm监控和故障处理工具

    java虚拟机性能监控常用命令 Sun JDK监控和故障处理命令有jps.jstat.jinfo.jmap.jhat.jstack . 1.jps jps:JVM Process Status Too ...

  7. 【算法分析】如何理解快慢指针?判断linked list中是否有环、找到环的起始节点位置。以Leetcode 141. Linked List Cycle, 142. Linked List Cycle II 为例Python实现

    引入 快慢指针经常用于链表(linked list)中环(Cycle)相关的问题.LeetCode中对应题目分别是: 141. Linked List Cycle 判断linked list中是否有环 ...

  8. CodeForces 908C. New Year and Curling 解题报告 Java

    1. 思路 这题实际上是个几何问题——两个外相切的圆,由勾股定理,他们的纵坐标有以下的规律: 则有$$y_{n+1} = y_{n} + \sqrt{(2r)^2 - (x_{n} - x_{n+1} ...

  9. Merkle Patricia Tree (MPT) 以太坊中的默克尔树

    本篇博文是自己学习mpt的过程,边学边记录,很多原理性内容非自己原创,好的博文将会以链接形式进行共享. 一.什么是mpt MPT是以太坊中的merkle改进树,基于基数树,即前缀树改进而来,大大提高了 ...

  10. NMAP-主机扫描

    1.全面扫描 2.扫描指定段 3.ping扫描 只进行ping操作,十分隐蔽 4.无ping扫描 适用于防火墙禁止ping 5.TCP SYN扫描 6.TCP ACK扫描 7.UDP扫描 8.ICMP ...