@[高精度, Prufer, 質因數分解]

Description

自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在

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

Input

第一行为\(N(0 < N <= 1000)\),

接下来\(N\)行,第\(i + 1\)行给出第\(i\)个节点的度数\(D_i\),如果对度数不要求,则输入\(- 1\)

Output

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

Sample Input

  1. 3
  2. 1
  3. -1
  4. -1

Sample Output

  1. 2

HINT

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

Solution

Prufer編碼的簡單應用.

Prufer是無根樹的一種編碼, 可以和無根樹之間形成一一對應關係. 生成Prufer編碼的方法很簡單, 大致過程如下(這都不是重點)

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

重點在於: 在Prufer編碼中, $$一個節點出現的次數 = 該點的度 - 1$$

有了這個性質, 這題就可以簡單地轉化為求Prufer編碼的排列組合數量.

【思考】怎么计算呢?就用排列组合的原理。我们先考虑不是-1的点。一共有N-2个格子,设当前的点要求为a1(已经减1了),我们可以放的总数是:C(N-2,a1)。这样占了a1个格子。下一个a2的情况就是C(N-2-a1,a2)。依次类推。

然后是-1的情况,我想了很长时间。

原来是这样想的:设最后还有K个格子,且有SUM个-1。我们用DP思路。f[i][j]表示放到最后K个格子中的第i个格子时,放第j种颜色(注意颜色是不下降的,最后只需乘上阶乘即可)的情况数。但是因为要用高精,DP会很麻烦。

感谢HHD大牛的指导:其实就是SUM^K。为什么?对于每一个格子,我们都可以放SUM种情况~~~~。

【优化】为了不超时,用质数表来优化。

實際上就是用分解質因數的方法來優化, 然後再用高精度計算最終答案.

  1. #include<cstdio>
  2. #include<cctype>
  3. #include<cstring>
  4. using namespace std;
  5. inline int read()
  6. {
  7. int x = 0, flag = 1;
  8. char c;
  9. while(! isdigit(c = getchar()))
  10. if(c == '-')
  11. flag *= - 1;
  12. while(isdigit(c))
  13. x = x * 10 + c - '0', c = getchar();
  14. return x * flag;
  15. }
  16. void println(int x)
  17. {
  18. if(x < 0)
  19. putchar('-'), x *= - 1;
  20. if(x == 0)
  21. putchar('0');
  22. int ans[10], top = 0;
  23. while(x)
  24. ans[top ++] = x % 10, x /= 10;
  25. for(; top; top --)
  26. putchar(ans[top - 1] + '0');
  27. putchar('\n');
  28. }
  29. const int N = 1 << 10;
  30. int top;
  31. int isprime[N], prime[N];
  32. void get_prime()
  33. {
  34. memset(isprime, - 1, sizeof(isprime));
  35. top = 0;
  36. for(int i = 2; i < N; i ++)
  37. if(isprime[i] == - 1)
  38. {
  39. isprime[i] = 1, prime[top ++] = i;
  40. for(int j = i << 1; j < N; j += i)
  41. isprime[j] = 0;
  42. }
  43. }
  44. int ans[N];
  45. void C(int n, int m)
  46. {
  47. for(int i = n; i > n - m; i --)
  48. {
  49. int p = i;
  50. for(int j = 0; j < top; j ++)
  51. while(! (p % prime[j]))
  52. ans[j] ++, p /= prime[j];
  53. }
  54. for(int i = m; i; i --)
  55. {
  56. int p = i;
  57. for(int j = 0; j < top; j ++)
  58. while(! (p % prime[j]))
  59. ans[j] --, p /= prime[j];
  60. }
  61. }
  62. struct Giant
  63. {
  64. int dig[1 << 20];
  65. int top;
  66. Giant()
  67. {
  68. top = 1;
  69. memset(dig, 0, sizeof(dig));
  70. dig[0] = 1;
  71. }
  72. };
  73. void operator *(Giant &x, int y)
  74. {
  75. for(int i = 0; i < x.top; i ++)
  76. x.dig[i] *= y;
  77. for(int i = 0; i < x.top; i ++)
  78. if(x.dig[i] > 9)
  79. x.dig[i + 1] += x.dig[i] / 10, x.dig[i] %= 10;
  80. while(x.dig[x.top])
  81. x.dig[x.top + 1] = x.dig[x.top] / 10,
  82. x.dig[x.top] %= 10,
  83. x.top ++;
  84. }
  85. void println(Giant &x)
  86. {
  87. for(int i = x.top; i; i --)
  88. putchar(x.dig[i - 1] + '0');
  89. putchar('\n');
  90. }
  91. Giant Ans;
  92. int main()
  93. {
  94. #ifndef ONLINE_JUDGE
  95. freopen("BZOJ1005.in", "r", stdin);
  96. freopen("BZOJ1005.out", "w", stdout);
  97. #endif
  98. get_prime();
  99. int n = read();
  100. int len = 0, cnt = 0;
  101. //len記錄prufer序列已經佔用的長度, cnt記錄尚未確定度的點數
  102. for(int i = 0; i < n; i ++)
  103. {
  104. int D = read();
  105. if((! D) || (D - 1 + len > n - 2))
  106. {
  107. println(0);
  108. return 0;
  109. }
  110. if(D == - 1)
  111. cnt ++;
  112. if(D > 0)
  113. C(n - 2 - len, D - 1), len += D - 1;
  114. }
  115. if(len < n - 2)
  116. {
  117. for(int i = 0; i < top; i ++)
  118. while(! (cnt % prime[i]))
  119. ans[i] += n - 2 - len, cnt /= prime[i];
  120. }
  121. for(int i = 0; i < top; i ++)
  122. for(int j = 0; j < ans[i]; j ++)
  123. Ans * prime[i];
  124. println(Ans);
  125. }

BZOJ1005明明的烦恼 Prufer + 分解質因數 + 高精度的更多相关文章

  1. bzoj1211树的计数 x bzoj1005明明的烦恼 题解(Prufer序列)

    1211: [HNOI2004]树的计数 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3432  Solved: 1295[Submit][Stat ...

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

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

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

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

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

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

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

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

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

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

  7. [bzoj1005][HNOI2008]明明的烦恼-Prufer编码+高精度

    Brief Description 给出标号为1到N的点,以及某些点最终的度数,允许在 任意两点间连线,可产生多少棵度数满足要求的树? Algorithm Design 结论题. 首先可以参考这篇文章 ...

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

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

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

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

随机推荐

  1. 简单了解hash

    hash,译为散列或哈希.就是把任意长度的输入(可变类型除外)经过hash算法,输出成固定长度的输出,该输出就是hash值.哈希值比原有的输出占用空间要小,但是不同的输出可能会hash出一样的值,所以 ...

  2. selenium2用AutoIt上传文件

    1.标签是input,如下图所示: WebElement e1= driver.findElement(By.id("load"));//输入要上传文件的地址e1.sendKeys ...

  3. Java-一个数组中的元素复制到另一个数组

    public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length).其中五个参数分别表示为: ...

  4. 【转】LAMP网站架构方案分析【精辟】

    [转]LAMP网站架构方案分析[精辟] http://www.cnblogs.com/mo-beifeng/archive/2011/09/13/2175197.html Xubuntu下LAMP环境 ...

  5. 设计模式之工厂模式 Factory实现

    simpleFactory //car接口 public interface Car { void run(); } //两个实现类 public class Audi implements Car{ ...

  6. 【软件工程】Word frequency program

    一.开始写代码前的规划: 1.尝试用C#来写,之前没有学过C#,对于C++也不熟,所以打算先花1天的时间学习C# 2.整个程序基本分为文件遍历.单词提取.单词匹配.排序.输出几个模块,各个模块大致时间 ...

  7. [转]物理CPU、CPU核数、逻辑CPU、超线程

    转自:http://wulc.me/2016/01/06/物理CPU.CPU核数.逻辑CPU.超线程/ 基本概念 物理CPU: 物理CPU就是插在主机上的真实的CPU硬件,在Linux下可以数不同的p ...

  8. js原型链继承的傻瓜式详解

    本文争取用最简单的语言来讲解原型链继承的OOP原理 0.如果对原型继承还没有大致了解,完全一头雾水,请先阅读 <JavaScript高级程序设计>第六章最后部分的寄生组合式继承 或者_廖雪 ...

  9. POJ 2051 Argus

    Argus Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8782   Accepted: 3985 Description ...

  10. 一步一步,完成sparkMLlib对日志文件的处理(1)

    https://blog.csdn.net/u012834750/article/details/81014997    初学第一天,当然是完成helloWorld啦,有点艰难,2个小时,在idea, ...