给定一棵无根树,假设它有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
思路:
首先,任选一个节点,设定为树的根。
用num[x]表示以节点x为根的子树的节点总数(包含x自身)
假如设定节点1为根,则先求出dp[1],表示所有节点到节点1的距离之和,
对根而言也是所有节点的深度之和。
若x是y的子结点,则有
dp[x] = dp[y] + (n-num[x]) - num[x];
因为x为根的子树的所有节点到x的距离比到y的距离少1,所以减num[x]
其余节点到x的距离比到y的距离多1,所以加 n-num[x]
做题感悟: 这题 一开始 我一直把重点放在随便一个节点身上,想着怎样才能推出一个万能的状态转移。。。但我忽略了,对于根,他所有的子树跟他的距离和就是他跟所有点的距离和。。。这可以算一个已知条件。。。而我一直找下面节点的状态。。。这时候又因为他的父节点推不出来一直想。。。 其实跟节点算个已知了。。应该根据这个往外推,我真zz。。。然后转移就是上面红字说的了。。。画个图想象一下就好。。。另外这题的跟节点是求每个节点的深度和。。。我这题是从下往上推得。。dp[fa] = dp[to] + cnt[to],因为父节点跟儿子节点之间这条连线要走cnt[to]遍。。。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
long long dp[maxn], n, deep[maxn], cnt[maxn];
vector<int> p[maxn];
void dfs(int x, int f)
{
cnt[x] = 1;
if(!p[x].size())
{
deep[x] = 0;
return ;
}
for(int i = 0; i < p[x].size(); i++)
{
int to = p[x][i];
if(to == f) continue;
dfs(to, x);
dp[x] += deep[to] + cnt[to];
deep[x] += deep[to] + cnt[to];
cnt[x] += cnt[to];
}
}
void dfs2(int x, int f)
{
for(int i = 0; i < p[x].size(); i++)
{
int to = p[x][i];
if(to == f) continue;
dp[to] = dp[x] - cnt[to] + n - cnt[to];
dfs2(to, x);
}
}
int main()
{
int u, v;
while(~scanf("%lld", &n))
{
for(int i = 1; i <= n; i++) p[i].clear();
memset(dp, 0, sizeof(dp));
memset(deep, 0, sizeof(deep));
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i < n; i++)
{
scanf("%d%d", &u, &v);
p[u].push_back(v);
p[v].push_back(u);
}
dfs(1, -1);
dfs2(1,-1);
for(int i = 1; i <= n; i++)
{
printf("%lld\n", dp[i]);
}
}
return 0;
}
其实直接求跟根节点的深度就好了啊。。。唉,zz如我
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 5;
long long dp[maxn], n, deep[maxn], cnt[maxn];
vector<int> p[maxn];
void dfs(int x, int f)
{
cnt[x] = 1;
// if(!p[x].size())
// {
// deep[x] = 0;
// return ;
// }
for(int i = 0; i < p[x].size(); i++)
{
int to = p[x][i];
if(to == f) continue;
deep[to] = deep[x] + 1; //求子节点的深度,是根据父节点转移而来的
dp[1] += deep[to];
dfs(to, x);
cnt[x] += cnt[to];
}
}
void dfs2(int x, int f)
{
for(int i = 0; i < p[x].size(); i++)
{
int to = p[x][i];
if(to == f) continue;
dp[to] = dp[x] - cnt[to] + n - cnt[to];
dfs2(to, x);
}
}
int main()
{
int u, v;
while(~scanf("%lld", &n))
{
for(int i = 1; i <= n; i++) p[i].clear();
memset(dp, 0, sizeof(dp));
memset(deep, 0, sizeof(deep));
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i < n; i++)
{
scanf("%d%d", &u, &v);
p[u].push_back(v);
p[v].push_back(u);
}
dfs(1, -1);
dfs2(1,-1);
for(int i = 1; i <= n; i++)
{
printf("%lld\n", dp[i]);
}
}
return 0;
}