Codeforces Round #513 E. Sergey and Subway 樹形dp (樹+dfs) 樹上任意兩點距離和
阿新 • • 發佈:2018-11-13
CF:
dfs and similar dp trees *2000
題意:
給定一個n個點的樹,(n最大2e5),如果原圖(樹)中有邊 u-v, v-w ,那麼現在你可以連一條邊u-w;
問任意兩點間最短距離的和;
思路:
開始想的對原樹dfs,用dp[i][2] 分別表示到i結點距離為偶數和奇數的最小花費,但是很麻煩。。。
其實:按照題目給定的要求,我們發現原來兩點間距離L,L為偶數-現在花費是L/2,L為奇數-現在花費是(L+1)/2;
正男則反??:相當於在題目給定的基礎上減少了一些花費,如上; 其實大小一樣
然後我們還知道可以O(n) 的求取樹上(任意兩點間距離)的總和sum;我們只要知道有多少個點對(設num個)間距離是奇數就好了,然後ans = (sum+num) / 2;
求距離為奇數的點對時,把樹看作奇數層偶數層就好了,這些點對一定是奇數層和偶數層分別一個點;
ps 求樹上任意兩點間距離的和就是跑dfs的時候,用cnt[u]表示以u為根結點子樹的大小(結點的個數),然後cnt[u] * (n-cnt[u])
相當於算邊的貢獻,這條邊就是u跟父結點的連邊;
#include<bits/stdc++.h> using namespace std; #define out fflush(stdout); #define fast ios::sync_with_stdio(0),cin.tie(0); #define FI first #define SE second typedef long long ll; typedef pair<ll,ll> P; const int maxn = 2e5 + 7; const int INF = 0x3f3f3f3f; const ll mod = 998244353; ll n; vector<int> vec[maxn]; ll ans = 0, num = 0; ll cnt[maxn]; void dfs(int id, int f, int ce) { if(ce) num++; cnt[id] = 1; for(auto i : vec[id]) { if(i == f) continue; dfs(i, id, ce^1); cnt[id] += cnt[i]; } ans += (cnt[id] * (n-cnt[id])); } int main() { scanf("%lld", &n); int u, v; for(int i = 1; i < n; ++i) { scanf("%d%d", &u, &v); vec[u].push_back(v); vec[v].push_back(u); } dfs(1, -1, 1); ans = (ans + num*(n-num)) / 2LL; printf("%lld\n", ans); return 0; }