两道题目大意都是根据每个点的度数来构建一棵无根树来确定有多少种构建方法

这里构建无根树要用到的是prufer序列的知识

先很无耻地抄袭了一段百度百科中的prufer序列的知识:

将树转化成Prufer数列的方法

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

以右边的树为例子,首先在所有叶子节点中编号最小的点是2,和它相邻的点的编号是3,将3加入序列并删除编号为2的点。接下来删除的点是4,5被加入序列,然后删除5,1被加入序列,1被删除,3被加入序列,此时原图仅剩两个点(即3和6),Prufer序列构建完成,为{3,5,1,3}

将Prufer数列转化成树的方法

设{a1,a2,..an-2}为一棵有n个节点的树的Prufer序列,另建一个集合G含有元素{1..n},找出集合中最小的未在Prufer序列中出现过的数,将该点与Prufer序列中首项连一条边,并将该点和Prufer序列首项删除,重复操作n-2次,将集合中剩余的两个点之间连边即可。
例子
仍为上面的树,Prufer序列为{3,5,1,3},开始时G={1,2,3,4,5,6},未出现的编号最小的点是2,将2和3连边,并删去Prufer序列首项和G中的2。接下来连的边为{4,5},{1,5},{1,3},此时集合G中仅剩3和6,在3和6之间连边,原树恢复
 
这样很容易可以发现通过构建n-2个数的数列就可以得到唯一的一棵无根树,当然如果要求根的不同的话,那么在种数的基础上乘以n就行了嘛,反正每个点都是能作为根的
仔细观察数列很容易发现,构建数列的过程中每次删除的都是度数为1的点,放入父亲,那就有点像拓扑排序的过程了
而最后留了两个数,也可以理解为从prufer序列中删除,那么删除的点都减少了一个度数,其他度数有多少都放进了prufer序列中
那么就说明prufer序列中某一个点出现的次数就是它本身的度数减1
再从父亲到儿子的角度思考的话,因为prufer序列有n-2个点,但树有n-1条边,最后一条边也删去的话,最后加入序列末尾的必然是最大值n
那么这个时候我们可以把整个图看作是以n为根的树,出现在prufer序列中的点的次数就是它在树上含有的儿子个数
 
 
对于这两道题目的计算来说的话,就相当于将能构成树的这些树进行全排列,得到排列的方法数就行了
(n-2)! / (w[1]!*w[2]!*w[3]!...) w[i]表示 i 的度数-1
这种题目需要注意到无法构建树的情况即可
 
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. #include <vector>
  5. using namespace std;
  6. #define pii pair<int,int>
  7. int n , w[] , num[];
  8. vector<pii> v[];
  9.  
  10. void solve(int m)
  11. {
  12. if(m==) return;
  13. int tmp = m;
  14. for(int i= ; i<=m ; i++){
  15. if(m%i==){
  16. int cnt = ;
  17. while(m%i==){
  18. m/=i;
  19. cnt++;
  20. }
  21. v[tmp].push_back(make_pair(i , cnt));
  22. }
  23. }
  24. if(m>) v[tmp].push_back(make_pair(m , ));
  25. // for(int i=0 ; i<v[tmp].size() ; i++) cout<<v[tmp][i].first<<" "<<v[tmp][i].second<<endl;
  26. }
  27. void init()
  28. {
  29. for(int i= ; i<= ; i++) solve(i);
  30. }
  31. void update(int k , int flag)
  32. {
  33. for(int i= ; i<v[k].size() ; i++){
  34. pii u=v[k][i];
  35. num[u.first]+=u.second*flag;
  36. }
  37. }
  38. void mul(long long &ans , int k , int tim)
  39. {
  40. for(int i= ; i<=tim ; i++) ans=ans*k;
  41. }
  42. int main()
  43. {
  44. // freopen("Sweet.in" , "r" , stdin);
  45. init();
  46. while(~scanf("%d" , &n)){
  47. memset(num , , sizeof(num));
  48. int sum= , flag=true;
  49. for(int i= ; i<=n ; i++){
  50. scanf("%d" , &w[i]);
  51. if(w[i]==) flag = false;
  52. sum+=w[i]-;
  53. for(int j= ; j<=w[i]- ; j++) update(j , -);
  54. }
  55. if(n== && w[]==){
  56. cout<<<<endl;
  57. continue;
  58. }
  59. if(sum!=n- || n== || flag==false){
  60. cout<<<<endl;
  61. continue;
  62. }
  63. for(int i= ; i<=n- ; i++) update(i , );
  64. long long ans = ;
  65. for(int i= ; i<=n- ; i++){
  66. if(num[i]) mul(ans , i , num[i]);
  67. }
  68. cout<<ans<<endl;
  69. }
  70. return ;
  71. }

bzoj1211

 
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <iostream>
  4. using namespace std;
  5. #define N 1005
  6. int n , a[N] , cnt[N];
  7. int ans[N*] , l1;
  8. int mul[N] , l2;
  9. int tmp[N*];
  10.  
  11. void fenjie(int x , int flag)
  12. {
  13. for(int i= ; i*i<=x ; i++){
  14. while(x%i==){
  15. cnt[i]+=flag;
  16. x/=i;
  17. }
  18. }
  19. if(x>) cnt[x]+=flag;
  20. }
  21.  
  22. void solveC(int a , int b) //C(n-2 , sum)
  23. {
  24. for(int i= ; i<=b ; i++) fenjie(i , -);
  25. for(int i=a ; i>a-b ; i--) fenjie(i , );
  26. }
  27.  
  28. void change(int x)
  29. {
  30. l2 = ;
  31. while(x){
  32. mul[l2++] = x%;
  33. x/=;
  34. }
  35. }
  36.  
  37. void cal()
  38. {
  39. memset(tmp , , sizeof(tmp));
  40. int len = ;
  41. for(int i= ; i<l2 ; i++){
  42. for(int j= ; j<l1 ; j++){
  43. int cur = i+j;
  44. len = max(len , cur+);
  45. tmp[cur] += ans[j]*mul[i];
  46. }
  47. }
  48. for(int i= ; i<len ; i++){
  49. if(tmp[i]>=){
  50. len= max(len , i+);
  51. tmp[i+] += tmp[i]/;
  52. tmp[i] %= ;
  53. }
  54. }
  55. for(int i= ; i<len ; i++) ans[i] = tmp[i] , l1 = len;
  56. }
  57.  
  58. void print()
  59. {
  60. for(int i=l1- ; i>= ; i--) printf("%d" , ans[i]);
  61. printf("\n");
  62. }
  63. int main()
  64. {
  65. // freopen("a.in" , "r" , stdin);
  66. while(~scanf("%d" , &n)){
  67. memset(cnt , , sizeof(cnt));
  68. int w = , sum = ;
  69. bool flag = true;
  70. for(int i= ; i<=n ; i++){
  71. scanf("%d" , &a[i]);
  72. if(a[i]==) flag=false;
  73. if(a[i]<) w++;
  74. else{
  75. sum+=a[i]-;
  76. for(int j= ; j<=a[i]- ; j++) fenjie(j , -);
  77. }
  78. }
  79. if(n== && a[]==){
  80. cout<<<<endl;
  81. continue;
  82. }
  83. solveC(n- , sum);
  84. if(n== || sum>n- || flag==false) cout<<<<endl;
  85. else{
  86. for(int i= ; i<=sum ; i++) fenjie(i, );
  87. int cnt = n--sum;
  88. for(int i= ; i<=cnt ; i++) fenjie(w , );
  89. }
  90. // for(int i=1 ; i<=10 ; i++) cnt[i]++;
  91. ans[] = , l1=;
  92. for(int i= ; i<=n ; i++){
  93. if(cnt[i]){
  94. change(i);
  95. for(int j= ; j<=cnt[i] ; j++){
  96. cal();
  97. }
  98. }
  99. }
  100. print();
  101. }
  102. return ;
  103. }

bzoj1005

bzoj 1005 1211 prufer序列总结的更多相关文章

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

    题目描述 输入输出格式 输入格式: 输入文件第一行是一个正整数n,表示树有n个结点.第二行有n个数,第i个数表示di,即树的第i个结点的度数.其中1<=n<=150,输入数据保证满足条件的 ...

  2. [BZOJ1005][HNOI2008]明明的烦恼 数学+prufer序列+高精度

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int N; ...

  3. 【BZOJ 1211】 1211: [HNOI2004]树的计数 (prufer序列、计数)

    1211: [HNOI2004]树的计数 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2468  Solved: 868 Description 一 ...

  4. [HNOI2008][bzoj 1005]明明的烦恼(prufer序列)

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

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

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

  6. BZOJ 1005 明明的烦恼(prufer序列+高精度)

    有一种东西叫树的prufer序列,一个树的与一个prufer序列是一一对应的关系. 设有m个度数确定的点,这些点的度为dee[i],那么每个点在prufer序列中出现了dee[i]-1次. 由排列组合 ...

  7. BZOJ 1005 明明的烦恼 Prufer序列+组合数学+高精度

    题目大意:给定一棵n个节点的树的节点的度数.当中一些度数无限制,求能够生成多少种树 Prufer序列 把一棵树进行下面操作: 1.找到编号最小的叶节点.删除这个节点,然后与这个叶节点相连的点计入序列 ...

  8. BZOJ 1005 prufer序列

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

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

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

随机推荐

  1. Windows消息机制概述

    消息是指什么?     消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程 ...

  2. 一种比较少见的C#代码段

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  3. 深入理解Java PriorityQueue

    PriorityQueue 本文github地址 Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析Prio ...

  4. 爷爷辈儿的AX

    你是否见过第一版的AXAPTA? @FlemmingLR 晒出了他收藏的老光盘. 这就是爷爷辈儿的AX——AXAPTA Version 1.0.

  5. jQuery 简单过滤选择器

    <!DOCTYPE HTML> <html> <head> <title> 使用jQuery基本过滤选择器 </title> <scr ...

  6. Posix消息队列

    转载于:http://blog.csdn.net/zx714311728/article/details/53197196 1.消息队列 消息队列可以认为是一个消息链表,消息队列是随内核持续的.队列中 ...

  7. popUpWindow 动画无法超出窗体的解决方案

    popupWindow 做动画时,当要求有一个放大动画时,动画无法超出窗体,给人的感觉是只有内容在放大,窗体不动. 这是由于窗口大小固定的原因,解决方案是加大popUpwindow的 大小. 一个比较 ...

  8. Java 泛型和通配符解惑

    转自:http://www.linuxidc.com/Linux/2013-10/90928.htm T  有类型 ?  未知类型 一.通配符的上界 既然知道List<Cat>并不是Lis ...

  9. [课程设计]Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)

    Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)  1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅 ...

  10. java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: NO)

    在更新项目之后,做了一定的改动后发现竟然报错了,刚才还好好的. java.sql.SQLException: Access denied for user 'root'@'localhost' (us ...