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