题意:
1405 树的距离之和
给定一棵无根树,假设它有n个节点,节点编号从1到n, 求1-n这n个节点,到其他n-1个节点的距离之和。
输入
第一行包含一个正整数n (n <= 100000),表示节点个数。
后面(n - 1)行,每行两个整数表示树的边。
输出
每行一个整数,第i(i = 1,2,…n)行表示所有节点到第i个点的距离之和。
输入样例
4
1 2
3 2
4 2
输出样例
5
3
5
5
思路:
树形dp
1.
任选一个节点,设定为树的根。
用num[x]表示以节点x为根的子树的节点总数(包含x自身)
假如设定节点1为根,则先求出dp[1],表示所有节点到节点1的距离之和,
对根而言也是所有节点的深度之和。
2.
若x是y的子结点,则有
dp[x] = dp[y] + (n-num[x]) - num[x];
因为x为根的子树的所有节点到x的距离比到y的距离少1,所以减num[x]
其余节点到x的距离比到y的距离多1,所以加 n-num[x]
注意:最后一个样例会卡long long,浪费了好长时间,要哭了
代码实现:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
vector<ll>G[maxn];
ll dp[maxn];
ll vis[maxn];
ll num[maxn];
ll n;
void dfs(ll root,ll dep){
ll len = G[root].size();
dp[1] += dep;
num[root] = 1;
if(len == 0){
return ;
}
for(ll i = 0;i < len;i++){
ll cur = G[root][i];
if(!vis[cur]){
vis[cur] = 1;
dfs(cur,dep + 1);
num[root] += num[cur];
}
}
}
void dfs1(ll now){
ll len = G[now].size();
if(len == 0){
return ;
}
for(ll i = 0;i < len;i++){
ll cur = G[now][i];
if(!vis[cur]){
vis[cur] = 1;
dp[cur] = dp[now] + (n - num[cur]) - num[cur];
dfs1(cur);
}
}
}
int main(){
scanf("%d",&n);
ll u,v;
for(int i = 1;i < n;i++){
scanf("%lld%lld",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
vis[1] = 1;
dfs(1,0);
memset(vis,0,sizeof(vis));
vis[1] = 1;
dfs1(1);
for(ll i = 1;i <= n;i++){
printf("%lld\n",dp[i]);
}
return 0;
}