题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5647

题解:

令dp[u][0]表示u所在的子树中所有的包含i的集合数,设u的儿子为vi,则易知dp[u][0]=(dp[v1][0]+1)*...*(dp[vk][0]+1)。

令dp[u][1]表示u所在的子树中所有的包含i的集合数的大小的和,则有dp[u][1]=dp[u][1]*(dp[v][0]+1)+dp[v][1]*dp[u][0];

其中dp[u][1]*(dp[v][0]+1)表示新引入v的(dp[v][0]+1)个组合的时候,左边已知的贡献值(dp[u][1],即已知的包含节点i的集合数的大小的和)增倍之后的量。

dp[v][1]*dp[u][0]则与上面刚好反过来,考虑对于已知的dp[u][0]种集合数,儿子v的贡献值增倍后的量。

则最后的答案为dp[1][1]+...+dp[n][1]

ps: 如果还不明白dp[u][0],dp[u][1]表示的含义,可以用代码跑一下简单的样例,把它们都打印出来,应该会好理解一些。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5.  
  6. const int maxn = 2e5 + ;
  7. const int mod = 1e9 + ;
  8. typedef long long LL;
  9.  
  10. struct Edge {
  11. int v, ne;
  12. Edge(int v, int ne) :v(v), ne(ne) {}
  13. Edge() {}
  14. }egs[maxn];
  15.  
  16. int head[maxn], tot;
  17. int n;
  18.  
  19. void addEdge(int u, int v) {
  20. egs[tot] = Edge(v, head[u]);
  21. head[u] = tot++;
  22. }
  23.  
  24. LL dp[maxn][];
  25.  
  26. void solve(int u) {
  27. dp[u][] = dp[u][] = ;
  28. int p = head[u];
  29. while (p != -) {
  30. Edge& e = egs[p];
  31. solve(e.v);
  32. dp[u][] = (dp[u][] * (dp[e.v][] + ) + dp[e.v][] * dp[u][]) % mod;
  33. dp[u][] = dp[u][] * (dp[e.v][] + )%mod;
  34. p = e.ne;
  35. }
  36. }
  37.  
  38. void init() {
  39. memset(head, -, sizeof(head));
  40. tot = ;
  41. }
  42.  
  43. int main() {
  44. int tc;
  45. scanf("%d", &tc);
  46. while (tc--) {
  47. init();
  48. scanf("%d", &n);
  49. for (int i = ; i <= n; i++) {
  50. int v;
  51. scanf("%d", &v);
  52. addEdge(v, i);
  53. }
  54. solve();
  55. LL ans = ;
  56. for (int i = ; i <= n; i++) {
  57. //printf("dp[%d][1]:%d\n", i,dp[i][1]);
  58. ans += dp[i][];
  59. ans %= mod;
  60. }
  61. printf("%lld\n", ans);
  62. }
  63. return ;
  64. }
  65.  
  66. /*
  67. 1 //testcase
  68. 3
  69. 1
  70. 1
  71. */

以下是按官方题解的思路写的代码,但是wa了,当(dp[u]+1)%mod==0的时候逆元就求不出来了。

这样写就t了,说明数据确实会出现这种情况

官方题解:

  1. #pragma comment(linker, "/STACK:102400000,102400000")
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. using namespace std;
  6. typedef long long LL;
  7.  
  8. const int maxn = 2e5 + ;
  9. const int mod = 1e9 + ;
  10.  
  11. struct Edge {
  12. int v, ne;
  13. Edge(int v,int ne):v(v),ne(ne){}
  14. Edge(){}
  15. }egs[maxn*];
  16.  
  17. int head[maxn], tot;
  18.  
  19. void addEdge(int u, int v) {
  20. egs[tot] = Edge(v, head[u]);
  21. head[u] = tot++;
  22. }
  23.  
  24. void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
  25. if (!b) { d = a; x = ; y = ; }
  26. else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
  27. // x = (x%mod + mod) % mod;
  28. // y = (y%mod + mod) % mod;
  29. }
  30.  
  31. LL invMod(LL a, LL b) {
  32. LL d, x, y;
  33. gcd(a, b, d, x, y);
  34. return (x%mod+mod)%mod;
  35. }
  36.  
  37. int n;
  38. LL dp[maxn];
  39. int fa[maxn];
  40. //自底向上
  41. void dfs1(int u) {
  42. dp[u] = ;
  43. int p = head[u];
  44. while (p != -) {
  45. Edge& e = egs[p];
  46. dfs1(e.v);
  47. dp[u] *= (dp[e.v] + ); dp[u] %= mod;
  48. p = e.ne;
  49. }
  50. }
  51. //自顶向下
  52. void dfs2(int u) {
  53. if (fa[u]) {
  54. LL tmp = dp[fa[u]] * invMod(dp[u] + , (LL)mod) % mod;
  55. dp[u] = dp[u] * (tmp + ) % mod;
  56. }
  57. int p = head[u];
  58. while(p != -) {
  59. Edge& e = egs[p];
  60. dfs2(e.v);
  61. p = e.ne;
  62. }
  63. }
  64.  
  65. void init() {
  66. fa[] = ;
  67. memset(dp, , sizeof(dp));
  68. memset(head, -, sizeof(head));
  69. tot = ;
  70. }
  71.  
  72. int main() {
  73. // freopen("data_in.txt", "r", stdin);
  74. int tc;
  75. scanf("%d", &tc);
  76. while (tc--) {
  77. scanf("%d", &n);
  78. init();
  79. for (int i = ; i <= n; i++) {
  80. int x;
  81. scanf("%d", &x);
  82. addEdge(x, i);
  83. fa[i] = x;
  84. }
  85. dfs1();
  86. dfs2();
  87. LL ans = ;
  88. for (int i = ; i <= n; i++) {
  89. ans += dp[i]; ans %= mod;
  90. }
  91. printf("%lld\n", ans);
  92. }
  93. return ;
  94. }
  95. /*
  96. */

但是!这种情况比较特殊,是可以单独处理一下的,对于节点u,如果在第一次dfs中它的儿子中有(dp[v]+1)%mod==0,那么说明在第一次dfs中dp[u]=0,也就是说dp[u]+1==1,u对它的父亲是没有贡献的!,那么在第二次dfs中,dp[u]实际保存的数就是整颗树中不包含u这颗子树的所有节点中包含u的父亲的集合的个数,所以只要算(dp[u]+1)*spec(spec表示v所有的兄弟的(dp[vi]+1)的乘积,不包含v本身),就可以了,具体看代码。

  1. #pragma comment(linker, "/STACK:102400000,102400000")
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. using namespace std;
  6. typedef long long LL;
  7.  
  8. const int maxn = 2e5 + ;
  9. const int mod = 1e9 + ;
  10.  
  11. struct Edge {
  12. int v, ne;
  13. Edge(int v,int ne):v(v),ne(ne){}
  14. Edge(){}
  15. }egs[maxn*];
  16.  
  17. int head[maxn], tot;
  18.  
  19. void addEdge(int u, int v) {
  20. egs[tot] = Edge(v, head[u]);
  21. head[u] = tot++;
  22. }
  23.  
  24. void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
  25. if (!b) { d = a; x = ; y = ; }
  26. else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
  27. }
  28. LL invMod(LL a, LL b) {
  29. LL d, x, y;
  30. gcd(a, b, d, x, y);
  31. return (x%mod+mod)%mod;
  32. }
  33.  
  34. int n;
  35. LL dp[maxn];
  36. int fa[maxn];
  37.  
  38. void dfs1(int u) {
  39. dp[u] = ;
  40. int p = head[u];
  41. while (p != -) {
  42. Edge& e = egs[p];
  43. dfs1(e.v);
  44. dp[u] *= (dp[e.v] + ); dp[u] %= mod;
  45. p = e.ne;
  46. }
  47. }
  48.  
  49. void dfs2(int u,LL spec) {
  50. if (fa[u]) {
  51. LL tmp;
  52. if((dp[u]+)%mod)
  53. tmp=dp[fa[u]] * invMod(dp[u] + , mod) % mod;
  54. else {
  55. tmp = (dp[fa[u]]+) * spec % mod;
  56. }
  57. dp[u] = dp[u] * (tmp + ) % mod;
  58. }
  59. int p = head[u]; spec = ;
  60. int po = -,flag=;
  61. while(p != -) {
  62. Edge& e = egs[p];
  63. if ((dp[e.v] + ) % mod == ) {
  64. if(po==-) po = e.v;
  65. else {
  66. flag = ;
  67. dfs2(e.v, );
  68. }
  69. }
  70. else {
  71. spec *= (dp[e.v] + ); spec %= mod;
  72. dfs2(e.v, spec);
  73. }
  74. p = e.ne;
  75. }
  76. if (po!=-) {
  77. if (!flag) dfs2(po, spec);
  78. else dfs2(po, );
  79. }
  80. }
  81.  
  82. void init() {
  83. fa[] = ;
  84. memset(dp, , sizeof(dp));
  85. memset(head, -, sizeof(head));
  86. tot = ;
  87. }
  88.  
  89. int main() {
  90. //freopen("data_in.txt", "r", stdin);
  91. int tc;
  92. scanf("%d", &tc);
  93. while (tc--) {
  94. scanf("%d", &n);
  95. init();
  96. for (int i = ; i <= n; i++) {
  97. int x;
  98. scanf("%d", &x);
  99. addEdge(x, i);
  100. fa[i] = x;
  101. }
  102. dfs1();
  103. dfs2(,);
  104. LL ans = ;
  105. for (int i = ; i <= n; i++) {
  106. ans += dp[i]; ans %= mod;
  107. }
  108. printf("%lld\n", ans);
  109. }
  110. return ;
  111. }
  112. /*
  113.  
  114. */

HDU 5647 DZY Loves Connecting 树形dp的更多相关文章

  1. HDU5647 DZY Loves Connecting 树形DP

    (先奉上jcvb大神的官方题解)BC 76 div 1 1002 对于每个结点i,统计出f[i]表示包含i的连通集有多少个,那么容易看出答案就是所有f[i]的和. 要计算f[i]是经典的树形DP问题. ...

  2. 【HDU 5647】DZY Loves Connecting(树DP)

    pid=5647">[HDU 5647]DZY Loves Connecting(树DP) DZY Loves Connecting Time Limit: 4000/2000 MS ...

  3. HDU 5646 DZY Loves Partition

    题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5646 bc:http://bestcoder.hdu.edu.cn/contests/con ...

  4. hdu 4514 并查集+树形dp

    湫湫系列故事——设计风景线 Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tot ...

  5. [HDU 5293]Tree chain problem(树形dp+树链剖分)

    [HDU 5293]Tree chain problem(树形dp+树链剖分) 题面 在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大. 分析 考虑树形dp,dp[x]表示以x为子树 ...

  6. hdu 4003 Find Metal Mineral 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 Humans have discovered a kind of new metal miner ...

  7. HDU 4123 Bob’s Race 树形dp+单调队列

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4123 Time Limit: 5000/2000 MS (Java/Others) Memory L ...

  8. HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...

  9. HDU 5758 Explorer Bo(树形DP)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5758 [题目大意] 给出一棵树,每条路长度为1,允许从一个节点传送到任意一个节点,现在要求在传送次 ...

随机推荐

  1. sender是什么意思C#

    /// <summary> /// sender就是事件发起者,e存储事件发起者的一些参数 /// 例如: /// private void button1_Click(object se ...

  2. 新建一个DataTable如何手动给其添加多条数据!

    早晨起来,想起昨天利用winform做类似于sqlserver数据库导入数据功能的时候,用到了新建一个DataTable手动给其添加多条数据,平时用不到,需要的时候想不起来了,这次不妨把他记下来.以下 ...

  3. C++获取得到图片的坐标和rgb

    #include <iostream> #include <fstream> #include <string> #include <windows.h> ...

  4. win php nginx 配置小细节

    win下配置php Nginx 首先 下载 php-Windows版本.下载Nginx Windows 版本 1> php.ini-production 修改为 php.ini 让其成为php的 ...

  5. linux服务器之LVS、Nginx和HAProxy负载均衡器对比

    linux服务器之LVS.Nginx和HAProxy负载均衡器对比. LVS特点:  1.抗负载能力强,使用IP负载均衡技术,只做分发,所以LVS本身并没有多少流量产生:  2.稳定性.可靠性好,自身 ...

  6. asp.net读取excel文件多种方法

    asp.net读取excel文件的三种方法示例,包括采用OleDB读取Excel文件.引用的com组件读取Excel文件.用文件流读取.   方法一:采用OleDB读取Excel文件 把Excel文件 ...

  7. Ubuntu通过APT配置开发环境

    apt-get install vim apt-get install ssh apt-get install apache2 apt-get install redis-server apt-get ...

  8. js 月历 时间函数 月份第一天 星期的判断

    返回值为0-6,其中返回值0为星期天:如同,php中的日期函数一样判断.

  9. 傅里叶变换 fft_generic halcon

    傅立叶变换(FT, Fourier Transform)的作用是将一个信号由时域变换到频域.其实就是把数据由横坐标时间.纵坐标采样值的波形图格式,转换为横坐标频率.纵坐标振幅(或相位)的频谱格式.变换 ...

  10. python zip函数介绍

    首先用help(zip)来看一下帮助文档: