樹形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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM