例题: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 ", dp[0]); //这里输出的是距离和 56 } 57 return 0; 58 }