树形DP--求树上任意两点间距离和

例题:HDU2376   HDU6446(2018CCPC网络赛)

思路:求任意两点间距离和可以转换为->路径长度乘经过路径次数的和。

求经过次数:设这条边两端的点,被经过的次数分别为A和B,那么这条边被经过的次数就是A*B,它对总距离和的贡献就是(A*B*此边长度)。

每条边两端点经过次数的计算,可以用一次dfs解决。

任取一点为根,在dfs的过程中,对每个点k记录其子树包含的点数(包括其自身),设点数为sum[k],则k的父亲一侧的点数即为N-sum[k]。这个统计可以和遍历同时进行。故时间复杂度为O(n)。

HDU2376:求完距离和,再除以总路径数N*(N-1)/2,即为最后所求

HDU6446:根据插点排序的思路,再乘以(N-1)! * 2,即为最后所求

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 const int maxn = 500005;
 6 
 7 int sum[maxn], n;
 8 ll dp[maxn];
 9 
10 struct Edge
11 {
12     int v, w;
13     Edge(int _v = 0, int _w = 0)
14     {
15         v = _v;
16         w = _w;
17     }
18 };
19 vector<Edge> tree[maxn];
20 
21 void dfs(int cur, int father)
22 {
23     sum[cur] = 1;
24     for(int i = 0; i < tree[cur].size(); i++)
25     {
26         int son = tree[cur][i].v;
27         ll len = tree[cur][i].w;
28         if(father == son)
29             continue;
30         dfs(son, cur);
31         sum[cur] += sum[son];
32         dp[cur] += dp[son] + (n-sum[son]) * sum[son] * len;
33     }
34 }
35 
36 int main()
37 {
38     int u, v, w, T;
39     scanf("%d", &T);
40     while(T--)
41     {
42         scanf("%d", &n);
43         for(int i = 0; i < n; i++)
44             tree[i].clear();
45         memset(sum, 0, sizeof(sum));
46         memset(dp, 0, sizeof(dp));
47         for(int i = 0; i < n-1; i++)
48         {
49             scanf("%d%d%d", &u, &v, &w);
50 
51             tree[u].push_back(Edge(v,w));
52             tree[v].push_back(Edge(u,w));
53         }
54         dfs(0, -1); //设0为根节点
55         printf("%I64d\n", dp[0]); //这里输出的是距离和
56     }
57     return 0;
58 }

猜你喜欢

转载自www.cnblogs.com/flyuz/p/9547103.html