1. 程式人生 > >Codeforces Round #513 E. Sergey and Subway 樹形dp (樹+dfs) 樹上任意兩點距離和

Codeforces Round #513 E. Sergey and Subway 樹形dp (樹+dfs) 樹上任意兩點距離和

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;
}