例題: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 }
