1. 程式人生 > >Codeforces 1060E(思維+貢獻法)

Codeforces 1060E(思維+貢獻法)

https://codeforces.com/contest/1060/problem/E

題意

給一顆樹,在原始的圖中假如兩個點連向同一個點,這兩個點之間就可以連一條邊,定義兩點之間的長度為兩點之間的最少邊數,求加邊之後任意兩點長度之和

思路

  • 一看到求任意兩點,知道需要用每條邊的貢獻計算(每條邊使用了多少次)

    每條邊的貢獻等於邊左邊的點數*邊右邊的點數

  • 然後就一直不知道怎麼解決加邊後的問題,不知道要標記哪些東西,怎麼減去
  • 單獨看一條路徑,加邊之後,
    • 假如邊數是偶數的話,邊數/2
    • 假如邊數是奇數的話,必定會走過一條跨過二分圖的邊
  • 所以只需要把連線二分圖的邊加上再除以2就得出答案了

#include<bits/stdc++.h>
#define M 200005
#define pb push_back
#define ll long long
using namespace std;
vector<int>g[M];
ll f[M],son[M];
int n,i,u,v;
ll ans;

void dfs(int u,int fa,int st){
    son[u]=1;f[st]++;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];if(fa==v)continue;
        dfs(v,u,st^1);
        son[u]+=son[v];
    }
    ans+=son[u]*(n-son[u]);
}
int main(){
    cin>>n;
    for(i=0;i<n-1;i++){scanf("%d%d",&u,&v);g[u].pb(v);g[v].pb(u);}
    dfs(1,0,0);
    cout<<(ans+f[0]*f[1])/2;
}