题目链接:

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]表示的含义,可以用代码跑一下简单的样例,把它们都打印出来,应该会好理解一些。

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; const int maxn = 2e5 + ;
const int mod = 1e9 + ;
typedef long long LL; struct Edge {
int v, ne;
Edge(int v, int ne) :v(v), ne(ne) {}
Edge() {}
}egs[maxn]; int head[maxn], tot;
int n; void addEdge(int u, int v) {
egs[tot] = Edge(v, head[u]);
head[u] = tot++;
} LL dp[maxn][]; void solve(int u) {
dp[u][] = dp[u][] = ;
int p = head[u];
while (p != -) {
Edge& e = egs[p];
solve(e.v);
dp[u][] = (dp[u][] * (dp[e.v][] + ) + dp[e.v][] * dp[u][]) % mod;
dp[u][] = dp[u][] * (dp[e.v][] + )%mod;
p = e.ne;
}
} void init() {
memset(head, -, sizeof(head));
tot = ;
} int main() {
int tc;
scanf("%d", &tc);
while (tc--) {
init();
scanf("%d", &n);
for (int i = ; i <= n; i++) {
int v;
scanf("%d", &v);
addEdge(v, i);
}
solve();
LL ans = ;
for (int i = ; i <= n; i++) {
//printf("dp[%d][1]:%d\n", i,dp[i][1]);
ans += dp[i][];
ans %= mod;
}
printf("%lld\n", ans);
}
return ;
} /*
1 //testcase
3
1
1
*/

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

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

官方题解:

 #pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL; const int maxn = 2e5 + ;
const int mod = 1e9 + ; struct Edge {
int v, ne;
Edge(int v,int ne):v(v),ne(ne){}
Edge(){}
}egs[maxn*]; int head[maxn], tot; void addEdge(int u, int v) {
egs[tot] = Edge(v, head[u]);
head[u] = tot++;
} void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
if (!b) { d = a; x = ; y = ; }
else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
// x = (x%mod + mod) % mod;
// y = (y%mod + mod) % mod;
} LL invMod(LL a, LL b) {
LL d, x, y;
gcd(a, b, d, x, y);
return (x%mod+mod)%mod;
} int n;
LL dp[maxn];
int fa[maxn];
//自底向上
void dfs1(int u) {
dp[u] = ;
int p = head[u];
while (p != -) {
Edge& e = egs[p];
dfs1(e.v);
dp[u] *= (dp[e.v] + ); dp[u] %= mod;
p = e.ne;
}
}
//自顶向下
void dfs2(int u) {
if (fa[u]) {
LL tmp = dp[fa[u]] * invMod(dp[u] + , (LL)mod) % mod;
dp[u] = dp[u] * (tmp + ) % mod;
}
int p = head[u];
while(p != -) {
Edge& e = egs[p];
dfs2(e.v);
p = e.ne;
}
} void init() {
fa[] = ;
memset(dp, , sizeof(dp));
memset(head, -, sizeof(head));
tot = ;
} int main() {
// freopen("data_in.txt", "r", stdin);
int tc;
scanf("%d", &tc);
while (tc--) {
scanf("%d", &n);
init();
for (int i = ; i <= n; i++) {
int x;
scanf("%d", &x);
addEdge(x, i);
fa[i] = x;
}
dfs1();
dfs2();
LL ans = ;
for (int i = ; i <= n; i++) {
ans += dp[i]; ans %= mod;
}
printf("%lld\n", ans);
}
return ;
}
/*
*/

但是!这种情况比较特殊,是可以单独处理一下的,对于节点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本身),就可以了,具体看代码。

 #pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL; const int maxn = 2e5 + ;
const int mod = 1e9 + ; struct Edge {
int v, ne;
Edge(int v,int ne):v(v),ne(ne){}
Edge(){}
}egs[maxn*]; int head[maxn], tot; void addEdge(int u, int v) {
egs[tot] = Edge(v, head[u]);
head[u] = tot++;
} void gcd(LL a, LL b, LL &d, LL &x, LL &y) {
if (!b) { d = a; x = ; y = ; }
else { gcd(b, a%b, d, y, x); y -= x*(a / b); }
}
LL invMod(LL a, LL b) {
LL d, x, y;
gcd(a, b, d, x, y);
return (x%mod+mod)%mod;
} int n;
LL dp[maxn];
int fa[maxn]; void dfs1(int u) {
dp[u] = ;
int p = head[u];
while (p != -) {
Edge& e = egs[p];
dfs1(e.v);
dp[u] *= (dp[e.v] + ); dp[u] %= mod;
p = e.ne;
}
} void dfs2(int u,LL spec) {
if (fa[u]) {
LL tmp;
if((dp[u]+)%mod)
tmp=dp[fa[u]] * invMod(dp[u] + , mod) % mod;
else {
tmp = (dp[fa[u]]+) * spec % mod;
}
dp[u] = dp[u] * (tmp + ) % mod;
}
int p = head[u]; spec = ;
int po = -,flag=;
while(p != -) {
Edge& e = egs[p];
if ((dp[e.v] + ) % mod == ) {
if(po==-) po = e.v;
else {
flag = ;
dfs2(e.v, );
}
}
else {
spec *= (dp[e.v] + ); spec %= mod;
dfs2(e.v, spec);
}
p = e.ne;
}
if (po!=-) {
if (!flag) dfs2(po, spec);
else dfs2(po, );
}
} void init() {
fa[] = ;
memset(dp, , sizeof(dp));
memset(head, -, sizeof(head));
tot = ;
} int main() {
//freopen("data_in.txt", "r", stdin);
int tc;
scanf("%d", &tc);
while (tc--) {
scanf("%d", &n);
init();
for (int i = ; i <= n; i++) {
int x;
scanf("%d", &x);
addEdge(x, i);
fa[i] = x;
}
dfs1();
dfs2(,);
LL ans = ;
for (int i = ; i <= n; i++) {
ans += dp[i]; ans %= mod;
}
printf("%lld\n", ans);
}
return ;
}
/* */

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. C#流总结(文件流、内存流、网络流、BufferedStream、StreamReader/StreamWriter、TextReader/TextWriter)

    一.文件流 FileStream类主要用于读写磁盘文件.常用于向磁盘存储数据或读取配置文件. 读取文件: //文件流:读取 FileStream fileStream = File.Open(@&qu ...

  2. JS模块化工具requirejs教程(一):初识requirejs

    随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作.模块复用.单元测试等等一系列复杂的需求 ...

  3. Kettle 合并记录报错!

    在Kettle的合并记录过程的时候,在“为了转换解除补丁开始 ”这一步的时候报错.具体错误如图所示: Kettle的转换如图所示: 问题原因:可能是你的数据库链接驱动和Kettle的版本不兼容. 解决 ...

  4. Java找出所有的水仙花数并输出

    水仙花数是三位数,它的各位数字的立方和等于这个三位数本身,例如:371=33+73+13,371就是一个水仙花数. 要判断是否是水仙花数,首先得得到它的每一位上的数.个位数即为对10取余:十位数为对1 ...

  5. php 判断table 是否存在 根据返回值继续下一步的操作

    根据sql命令创建数据库或者数据表时候,判断库或者表是否存在比较重要. //要创建的表是否已经存在 function isHaveTable( $dbName,$tableN, $con)  //数据 ...

  6. C# 调用系统API 内核 简单样例

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.R ...

  7. mysql数据库开发规范

    对规范的遵守可用二八原则,不要教条.为满足实际需求 可忽视部分规范. 1.索引规范 *目标 |--利用最小的索引成本找到需要的行记录 *原则 |--做前缀匹配 |--选择区分度高的列做前缀索引列 |- ...

  8. 文件系统 第八次迭代 VFS相关说明

    麻烦访问evernote链接 http://www.evernote.com/shard/s133/sh/53e5b5ac-1192-4910-8bd5-6886218562af/59516c32a5 ...

  9. 菜鸟学习Spring——60s学会Spring与Hibernate的集成

    一.概述. Spring与Hibernate的集成在企业应用中是很常用的做法通过Spring和Hibernate的结合能提高我们代码的灵活性和开发效率,下面我就一步一步的给大家讲述Spring如何和H ...

  10. 删除redo所有日志,数据库无法启动

    半夜在itpub上看到有人发贴,说不小心删除了redo所有日志,导致数据库无法启动,因此模拟了一下.   如下: OS:  Oracle Linux Server release 5.7 DB:  O ...