51NOD1405树的距离之和
题意就是给定一棵无根树,假设它有n个节点,节点编号从1到n, 求任意两点之间的距离(最短路径)之和。
首先由于这里树上的距离都是1,所以祖先与子孙的距离就是深度之差,我们可以一次树形dp求出每个子树包含的点的个数,并统计每个节点的深度,这样所有点距离根节点的最短距离就是所有点的深度之和,之后我们想如何向下
D
P
DP
DP,假设父亲的答案为
d
p
[
y
]
dp[y]
dp[y],儿子的答案为
d
p
[
x
]
dp[x]
dp[x],儿子的子树内节点个数为
n
u
m
[
x
]
num[x]
num[x],若所有节点到y的最短距离之和为dp[y],则有
d
p
[
x
]
=
d
p
[
y
]
−
−
n
u
m
[
x
]
+
(
n
−
n
u
m
[
x
]
)
,
B
e
c
a
u
s
e
:
x
子
树
内
的
点
距
离
和
−
1
,
x
子
树
外
的
点
距
离
和
+
1
dp[x]=dp[y]--num[x]+(n-num[x]),Because:x子树内的点距离和-1,x子树外的点距离和+1
dp[x]=dp[y]−−num[x]+(n−num[x]),Because:x子树内的点距离和−1,x子树外的点距离和+1
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int dept[maxn];
ll dp[maxn];
int num[maxn];
int n;
vector<int> v[maxn];
void dfs(int x,int rt,int dep)
{
dept[x]=dep;
num[x]=1;
for(int i=0;i<v[x].size();i++)
{
int to=v[x][i];
if(rt==to) continue;
dfs(to,x,dep+1);
num[x]+=num[to];
}
return ;
}
void dfs2(int x,int rt)
{
for(int i=0;i<v[x].size();i++)
{
int to=v[x][i];
if(rt==to) continue;
dp[to]=dp[x]+n-2*num[to];
dfs2(to,x);
}
return ;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1,-1,0);
for(int i=1;i<=n;i++) dp[1]+=dept[i];
dfs2(1,-1);
for(int i=1;i<=n;i++) cout<<dp[i]<<endl;
return 0;
}