1005: [HNOI2008]明明的烦恼

Description

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

Input

第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

Output

一个整数,表示不同的满足要求的树的个数,无解输出0

Sample Input

3

1

-1

-1

Sample Output

2

HINT

两棵树分别为1-2-3;1-3-2


前置知识:

做这题之前我们需要先了解Prufer Sequence,即普吕弗序列
   对于一颗完全已知,有n个节点,且所有的节点都有一个标号的前提下,我们对一棵树的叶子结点(即是度数为1的节点)按照其标号从小到大消去,每消去一个节点,我们就将它的父节点的编号记入一个序列中,直到树只剩下一条边时,得到一段长为n-2的唯一可以表示这棵树的序列,既是Prufer Sequence

举个例子:

现在我们有下图这么一棵树

我们对其进行如上操作就可以得到这么一段序列

1 2 3 4 5
4 4 4 5 5

对上图及表,我们似乎可以发现规律:

在普吕弗序列中点出现的次数等于这个点在树中的度数-1

由于所有的普吕弗序列和树唯一相对应,且有如上性质,因此,在给出了一棵树的度数之后想要求得树的数量,就可以转化为,求序列的组合的数量
那么就是一个简单的排列组合问题了

设无度数要求的点数是num,有要求的点数是poi,有要求的点在序列中出现的次数为sum,点的总数是n,那么答案就是:
ans=An−2sum∏i=1poi(du[i]−1)!∗numn−sum−2 ans= \frac {A^{sum}_{n-2}}{\prod_{i=1}^{poi}(du[i]-1)!}*num^{n-sum-2} ans=∏i=1poi​(du[i]−1)!An−2sum​​∗numn−sum−2

值得注意的是当只有一个节点时,需要特判,假如这个点度数不为0则无法构成树,而点大于1时,不能有点的度数为0,且必须出现的数的次数必须小于n-2
由于最终的数会很大,所以我们需要使用大数


AC代码

/**************************************************************Problem: 1005User: FlyWhiteLanguage: C++Result: AcceptedTime:84 msMemory:1304 kb
****************************************************************/#include<cstring>
#include<string>
#include<iostream>
#include<cstdio>
using namespace std;
const int size=1005;
struct BigInt{const static int mod=10000;const static int DLEN=4;int a[4005],len;BigInt(){memset(a,0,sizeof(a));len=1;}BigInt(int v){memset(a,0,sizeof(a));len=0;do{a[len++]=v%mod; v/=mod;}while(v);}BigInt(const char s[]){memset(a,0,sizeof(a));int L=strlen(s);len=L/DLEN;if(L%DLEN) len++;int index=0;for(int i=L-1;i>=0;i-=DLEN){int t=0;int k=i-DLEN+1;if(k<0) k=0;for(int j=k;j<=i;j++){t=t*10+s[j]-'0';}a[index++]=t;} }BigInt operator+ (const BigInt &b) const{BigInt res;res.len=max(len,b.len);for(int i=0;i<=res.len;i++){res.a[i]=0;}for(int i=0;i<res.len;i++){res.a[i]+=((i<len)?a[i]:0)+((i<b.len)?b.a[i]:0);res.a[i+1]+=res.a[i]/mod;res.a[i]%=mod;}if(res.a[res.len]>0) res.len++;return res;}BigInt operator *(const BigInt &b) const {BigInt res;for(int i=0;i<len;i++){int up=0;for(int j=0;j<b.len;j++){int temp=a[i]*b.a[j]+res.a[i+j]+up;res.a[i+j]=temp%mod;up=temp/mod;}if(up!=0) res.a[i+b.len]=up;}res.len=len+b.len;while(res.a[res.len-1]==0&&res.len>1) res.len--;return res; }void output(){printf("%d",a[len-1]);for(int i=len-2;i>=0;i--){printf("%04d",a[i]);}printf("\n");}
};
int du[size];
int tim[size];
bool prime[size];
int p[size],tot;
void init_prime(int n)
{tot=0;fill(prime,prime+size,true);for(int i=2;i<=n;i++){if(prime[i] ) p[tot++]=i;for(int j=0;j<tot&&i*p[j]<n;j++){prime[i*p[j]]=false;if(i%p[j]==0) break;}}
}
void solve(int num,int op)
{for(int i=2;i<=num;i++){int w=i;for(int j=0;j<tot;j++){if(w%p[j]==0)while(w%p[j]==0){w/=p[j];tim[j]+=op;}}}
}
int main()
{int n;scanf("%d",&n);memset(tim,0,sizeof(tim));init_prime(n);if(n==1){int v;scanf("%d",&v);if(v>0) printf("0\n");else printf("1\n");return 0;}int sum=0;int num=0;for(int i=1;i<=n;i++){scanf("%d",&du[i]);if(du[i]==0){printf("0\n");return 0;}if(du[i]==-1){num++;continue;}du[i]--;sum+=du[i];}if(sum>n-2) {printf("0\n");return 0;}BigInt ans(1);solve(n-2,1);if(n-sum-2>1)solve(n-sum-2,-1);for(int i=1;i<=n;i++){if(du[i]>1)solve(du[i],-1);}int w=num;int op=n-sum-2;for(int j=0;j<tot;j++){if(w%p[j]==0)while(w%p[j]==0){w/=p[j];tim[j]+=op;}}for(int i=0;i<tot;i++){BigInt N(p[i]);while(tim[i]--)ans=ans*(N);}ans.output();
}

转载于:https://www.cnblogs.com/fly-white/p/10092691.html

【Prufer Sequence +简单排列组合】bzoj 1005: [HNOI2008]明明的烦恼相关推荐

  1. BZOJ 1005: [HNOI2008]明明的烦恼

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

  2. bzoj 1005: [HNOI2008]明明的烦恼(prufer数列)

    1005: [HNOI2008]明明的烦恼 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 5171  Solved: 2021 [Submit][St ...

  3. BZOJ.1005.[HNOI2008]明明的烦恼(Prufer 高精 排列组合)

    题目链接 若点数确定那么ans = (n-2)!/[(d1-1)!(d2-1)!...(dn-1)!] 现在把那些不确定的点一起考虑(假设有m个),它们在Prufer序列中总出现数就是left=n-2 ...

  4. python 排列组合速度_Python实现的简单排列组合算法示例

    本文实例讲述了Python实现的简单排列组合算法.分享给大家供大家参考,具体如下: 1.python语言简单.方便,其内部可以快速实现排列组合算法,下面做简单介绍 2.一个列表数据任意组合 主要是利用 ...

  5. python写排列组合_Python实现的简单排列组合算法示例

    本文实例讲述了Python实现的简单排列组合算法.分享给大家供大家参考,具体如下: 1.python语言简单.方便,其内部可以快速实现排列组合算法,下面做简单介绍 2.一个列表数据任意组合 主要是利用 ...

  6. php 组合算法,PHP简单排列组合算法示例分享

    本文主要和大家介绍了PHP实现的简单排列组合算法,结合具体应用实例分析了排列组合算法的实现与使用技巧,需要的朋友可以参考下,希望能帮助到大家. 一.问题: 给你一个40斤的西瓜,给3个人分,有多少种分 ...

  7. 【BZOJ 1005】[HNOI2008]明明的烦恼 【Prufer序列】

    (1) 树的prufer编码的实现[1] 不断删除树中度数为1的最小序号的点,并输出与其相连的节点的序号直至树中只有两个节点 对应的prufer序列就是3,5,1,3. Data.in 6 5 1 3 ...

  8. bzoj1005 [HNOI2008]明明的烦恼 prufer+组合数学

    直接考虑prufer序列,每个度数-1就是再n-2的序列中出现的次数 对于不确定的-1,每个没有填的位置都是有可能填的,所以直接离散用乘法原理 对于确定的就是组合数选位置,注意要开高精 对于高精除法的 ...

  9. 【洛谷2624】[HNOI2008] 明明的烦恼(Python+利用prufer序列结论求解)

    点此看题面 大致题意: 给你某些点的度数,其余点度数任意,让你求有多少种符合条件的无根树. \(prufer\)序列 一道弱化版的题目:[洛谷2290][HNOI2004] 树的计数. 这同样也是一道 ...

  10. BZOJ1005 [HNOI2008]明明的烦恼

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

最新文章

  1. 高并发系统三大利器之限流
  2. 鸟哥的Linux私房菜(基础篇)- 第六章、Linux 的文件权限与目录配置
  3. c语言常用术语,保证让你大开眼界
  4. ARM中的ldr指令与adr、ldr伪指令之间的区别
  5. 副本验证失败,指定域文件复制服务(FRS)复制SYSVOL共享。FRS弃用。
  6. python判断数据在某个区间
  7. html代码如何查看视频,Web前端
  8. 使用XMLHttpRequest实现AJAX
  9. 网络编程学习记录-1
  10. [在windows上使用Unix工具]cygwin
  11. 简单的springBoot集成jedis
  12. 信号的时频分析MATLAB,基于matlab的信号时频分析仿真
  13. CNC数控加工中心编程好学吗
  14. 图及其应用实验报告 c语言,图的应用 实验报告
  15. 【无标题】绝对定位的特点
  16. 研究人员在西部数据存储设备上发现硬编码后门
  17. 百练4124:海贼王之伟大航路
  18. Seabron作图:
  19. 计算机等级ps考试1试题,计算机等级考试一级PS考前试题及答案一
  20. 基于JavaScript的简易计算器(可处理连续加减乘除运算)

热门文章

  1. 通过实例学习编写需求文档 【转】
  2. curl如何发送json数据?如何发送form数据?python的restfull又该如何获取这些数据?...
  3. PHP获取数组中重复值的键值
  4. oracle 分组后多行合并为一行
  5. 关于ASP调用.net WebService 的标准例子
  6. springboot 集成 freemarker
  7. golang 在 windows 下编译出 linux 二进制可执行文件的软件套装合集 [go 1.7.3环境]
  8. 安装MYSql Windows7下MySQL5.5.20免安装版的配置
  9. android中ImageView、ImageButton、Button之间的区别
  10. 相关矩阵可视化 – corrplot() 绘图