1. 程式人生 > >Luogu 1268 樹的重量

Luogu 1268 樹的重量

one 新的 () ext sed 每一個 構造 pac pen

並不會這種構造題。

首先可以隨意把一個葉子結點當作根,題目告訴了我們這樣子不會改變答案。

然後我們考慮一個一個把葉子結點連到這一棵樹裏面去,對於每一個葉子結點,我們可以把它對答案的貢獻看作它向根的連邊的長度減去已經計算過的長度,相當於從一條已經連過的邊拉出一條新的鏈把它連到樹裏面去,容易發現只要對每一個連過的結點算一算貢獻取最小值就可以了。

思考為什麽這樣子是對的。

隨便畫一個樹:

技術分享圖片

如圖,$1$為根,假設$2,3,4,7$都已經算過了,現在要計算$5$的貢獻,那麽有:

$(2, 5) = (10, 11, 12, 5)$

$(3, 5) = (11, 12, 5)$

$(4, 5) = (12, 5)$

$(7, 5) = (1, 10, 11, 12, 5)$。

發現這個點到其他已經計算過的點的拉出的鏈其實是包含關系的,而多出來的部分其實在之前已經算過了,所以只要取最小值就是這個點的貢獻了。

考慮一下怎麽計算點的貢獻,我們設根為$rt$,現在要從根到$x$的路徑上拉出一條鏈把$y$連到樹裏面去,記$lca(x, y) = z$,

對照上面的圖,發現貢獻$res = dis(y, rt) - dis(z, rt) = (dis(y,rt) - dis(x, rt) + dis(x, y)) / 2$。

時間復雜度$O(Tn^{2})$。

Code:

技術分享圖片
#include <cstdio>
#include 
<cstring> using namespace std; const int N = 55; const int inf = 1 << 30; int n, d[N][N]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > 9 || ch < 0; ch = getchar()) if(ch == -) op = -1; for(; ch >= 0 && ch <=
9; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void chkMin(int &x, int y) { if(y < x) x = y; } int main() { for(; ; ) { read(n); if(n == 0) break; for(int i = 1; i <= n; i++) for(int j = i + 1; j <= n; j++) read(d[i][j]), d[j][i] = d[i][j]; int ans = d[1][2]; for(int i = 3; i <= n; i++) { int res = inf; for(int j = 1; j < i; j++) chkMin(res, (d[1][i] - d[1][j] + d[i][j]) / 2); ans += res; } printf("%d\n", ans); } return 0; }
View Code

Luogu 1268 樹的重量