标签:
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 + 10; 7 const int mod = 1e9 + 7; 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][2]; 25 26 void solve(int u) { 27 dp[u][0] = dp[u][1] = 1; 28 int p = head[u]; 29 while (p != -1) { 30 Edge& e = egs[p]; 31 solve(e.v); 32 dp[u][1] = (dp[u][1] * (dp[e.v][0] + 1) + dp[e.v][1] * dp[u][0]) % mod; 33 dp[u][0] = dp[u][0] * (dp[e.v][0] + 1)%mod; 34 p = e.ne; 35 } 36 } 37 38 void init() { 39 memset(head, -1, sizeof(head)); 40 tot = 0; 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 = 2; i <= n; i++) { 50 int v; 51 scanf("%d", &v); 52 addEdge(v, i); 53 } 54 solve(1); 55 LL ans = 0; 56 for (int i = 1; i <= n; i++) { 57 //printf("dp[%d][1]:%d\n", i,dp[i][1]); 58 ans += dp[i][1]; 59 ans %= mod; 60 } 61 printf("%lld\n", ans); 62 } 63 return 0; 64 } 65 66 /* 67 1 //testcase 68 3 69 1 70 1 71 */
HDU 5647 DZY Loves Connecting 树形dp
标签:
原文地址:http://www.cnblogs.com/fenice/p/5472417.html