基准时间限制:1 秒 空间限制:131072 KB 分值: 40
难度:4级算法题
给定一棵无根树,假设它有n个节点,节点编号从1到n, 求任意两点之间的距离(最短路径)之和。
Input
第一行包含一个正整数n (n <= 100000),表示节点个数。 后面(n - 1)行,每行两个整数表示树的边。
Output
每行一个整数,第i(i = 1,2,...n)行表示所有节点到第i个点的距离之和。
Input示例
4 1 2 3 2 4 2
Output示例
5 3 5 5
思路:
(好难啊)思路源自:http://blog.csdn.net/to_be_better/article/details/50620434
1、虽然是一个无根树,但是我们可以设定节点1;
2、首先第一遍Dfs出一个数组num【i】,对应表示以第i个节点为根的子树内一共包含多少个节点(当然包括i这个节点);
同时我们可以在第一次Dfs的过程中,维护一个dp【1】,其表示其他所有节点到节点1的距离。
3、那么接下来我们再从节点1一次Dfs,同时维护dp状态转移方程:dp【v】=dp【u】-num【v】+(n-num【v】);
其中所有节点到u的距离作为基础,
①因为此时点向子树挪动距离为1,那么其v的子树中的所有节点到v的距离比到u的距离小1,那么对应减去。
②因为此时点向子树挪动距离为1,那么其u的上边的树上的所有节点到v的距离比到u的距离大1,那么对应加上。
4、注意会爆int。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
#define ll __int64
int num[105000];
int vis[105000];
ll dp[105000];
vector<int >mp[105000];
int n;
int Dfs(int u,int sum)
{
dp[1]+=sum;
num[u]=1;
vis[u]=1;
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(vis[v]==0)
{
Dfs(v,sum+1);
num[u]+=num[v];
}
}
}
void Dfs_dp(int u)
{
vis[u]=1;
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(vis[v]==0)
{
dp[v]=dp[u]-num[v]+(n-num[v]);
Dfs_dp(v);
}
}
}
int main()
{
while(~scanf("%d",&n))
{
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
mp[x].push_back(y);
mp[y].push_back(x);
}
memset(vis,0,sizeof(vis));
Dfs(1,0);
memset(vis,0,sizeof(vis));
Dfs_dp(1);
for(int i=1;i<=n;i++)
{
printf("%I64d\n",dp[i]);
}
}
}