51nod 1405 树的距离之和 (两次dfs,树形dp)
2016-02-23 23:21
369 查看
http://www.51nod.com/contest/problem.html#!problemId=1405
数学题,也可以说dp,不太难。
(1)我们给树规定一个根。假设所有节点编号是0-(n-1),我们可以简单地把0当作根,这样下来父子关系就确定了。
(2)定义数组num[x]表示以节点x为根的子树有多少个节点,dp[x]是我们所求的——所有节点到节点x的距离之和。
(3)在步骤(1)中,其实我们同时可以计算出 num[x],还可以计算出每个节点的深度(每个到根节点0的距离),累加全部节点深度得到的其实就是是dp[0]。
(4) 假设一个非根节点x,它的父亲节点是y, 并且dp[y]已经计算好了,我们如何计算dp[x]?
以x为根的子树中那些节点,到x的距离比到y的距离少1, 这样的节点有num[x]个。
其余节点到x的距离比到y的距离多1,这样的节点有(n - num[x])个。
于是我们有 dp[x] = dp[y] - num[x] + (n - num[x])
= dp[y] + n - num[x] * 2
因为树的根节点dp[0]在步骤(3)已经计算出来了,根据所有的父子关系和这个上式,我们可以按照顺序计算出整个dp数组。
注意点: 重要的步骤都是简单的dfs,但是一半递归实现可能导致堆栈溢出。
数学题,也可以说dp,不太难。
(1)我们给树规定一个根。假设所有节点编号是0-(n-1),我们可以简单地把0当作根,这样下来父子关系就确定了。
(2)定义数组num[x]表示以节点x为根的子树有多少个节点,dp[x]是我们所求的——所有节点到节点x的距离之和。
(3)在步骤(1)中,其实我们同时可以计算出 num[x],还可以计算出每个节点的深度(每个到根节点0的距离),累加全部节点深度得到的其实就是是dp[0]。
(4) 假设一个非根节点x,它的父亲节点是y, 并且dp[y]已经计算好了,我们如何计算dp[x]?
以x为根的子树中那些节点,到x的距离比到y的距离少1, 这样的节点有num[x]个。
其余节点到x的距离比到y的距离多1,这样的节点有(n - num[x])个。
于是我们有 dp[x] = dp[y] - num[x] + (n - num[x])
= dp[y] + n - num[x] * 2
因为树的根节点dp[0]在步骤(3)已经计算出来了,根据所有的父子关系和这个上式,我们可以按照顺序计算出整个dp数组。
注意点: 重要的步骤都是简单的dfs,但是一半递归实现可能导致堆栈溢出。
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <vector> #pragma comment(linker, "/STACK:10240000,10240000")//递归太深,导致爆栈,所以使用扩栈语句 using namespace std; typedef long long ll; const int N = 100000 + 5; vector<int>vv ; int num ,n; ll dp ; int dfs(int u,int pre,int d) { int len=vv[u].size(); num[u]=1; dp[1]+=d; for(int i=0;i<len;i++) { if(vv[u][i]==pre) continue; num[u]+=dfs(vv[u][i],u,d+1); } return num[u]; } void dfs2(int pre,int u) { dp[u]=dp[pre]+n-2*num[u]; int len=vv[u].size(); for(int i=0;i<len;i++) { if(vv[u][i]==pre) continue; dfs2(u,vv[u][i]); } } int main() { int i,j,u,v; scanf("%d",&n); for(i=1;i<n;i++) { scanf("%d%d",&u,&v); vv[u].push_back(v); vv[v].push_back(u); } dfs(1,0,0); for(i=0;i<vv[1].size();i++) { dfs2(1,vv[1][i]); } for(i=1;i<=n;i++) printf("%I64d\n",dp[i]); return 0; }
相关文章推荐
- 3个数和为0
- 51Nod 大数乘法
- 51Nod A^BmodC
- 51Nod 逆序数
- 51NOD 1020 逆序排列
- 51nod(1264)——线段相交
- 【排序】51NOD 1001-数组中和等于K的数对
- 51Nod 1212 无向图最小生成树
- 51Nod 1085 背包问题
- 51 Nod 完美字符串
- [51nod1227]平均最小公倍数(莫比乌斯反演+杜教筛)
- 51nod - 1672 线段树(插队问题变形)
- 51Nod - 1821 思维题 + 并查集 + 二分
- 51Nod - 1478 单调栈 + 二分
- 51Nod - 1376 dp
- 51Nod - 1407 容斥原理 + dp
- 51Nod - 1406 dp
- 51Nod - 1682 哈希 + 乱搞
- 51Nod - 1781 dp + 线段树 + 离散化
- 51Nod - 1503 多线程dp + 背包思想优化