题目大意:两个树,连一条边使得最小
题目思路:
根据树的重心的性质:
树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
我们只要找出两棵树中,每棵树的所有点到一个点的距离和最小就可以了。
也就是找重心连接起来。
然后dfs3遍历每条边算贡献,也就是这条边左边节点个数*右边节点个数就好了。
dfs0 :分离两棵树
dfs1 :求第一棵树的重心
dfs2 :求第二棵树的重心
dfs3:算每条边贡献
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
int vis[MAXN];
vector<int>v[MAXN];
void dfs0(int x,int fa)
{
//cout<<x<<endl;
int len=v[x].size();
for(int i=0;i<len;i++){
int to=v[x][i];
if(to!=fa){
vis[to]=1;
dfs0(to,x);
}
}
}
int num1=0,num2=0;
int v1[MAXN],sz1[MAXN],pos1,ans1;
void dfs1(int x)
{
//cout<<x<<endl;
v1[x]=1;sz1[x]=1;
int max_part = 0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(v1[y])continue;
dfs1(y);
sz1[x]+=sz1[y];
max_part=max(max_part,sz1[y]);
}
max_part=max(max_part,num1-sz1[x]);
if(max_part < ans1){
ans1=max_part;
pos1=x;
}
}
int v2[MAXN],sz2[MAXN],pos2,ans2;
void dfs2(int x)
{
v2[x]=1;sz2[x]=1;
int max_part = 0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(v2[y])continue;
dfs2(y);
sz2[x]+=sz2[y];
max_part=max(max_part,sz2[y]);
}
max_part=max(max_part,num2-sz2[x]);
if(max_part < ans2){
ans2=max_part;
pos2=x;
}
}
ll ans=0;
int n;
int sz[MAXN];
void dfs3(int x)
{
sz[x]=1;
int len=v[x].size();
for(int i=0;i<len;i++){
int to=v[x][i];
if(sz[to])continue;
dfs3(to);
ans+=(ll)(sz[to])*(n-sz[to]);
sz[x]+=sz[to];
}
}
int main()
{
ans1=ans2=0x7fffffff;
scanf("%d",&n);
for(int i=1;i<=n-2;i++){
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
vis[1]=1;
dfs0(1,0);
int st;
for(int i=1;i<=n;i++){
if(!vis[i]){
st=i;num2++;
}
else num1++;
}
dfs1(1);
dfs2(st);
//cout<<st<<endl;
v[pos1].push_back(pos2);
v[pos2].push_back(pos1);
dfs3(1);
//cout<<pos1<<" "<<pos2<<endl;
//cout<<num1<<" "<<num2<<endl;
printf("%lld\n",ans);
}
/*
10
1 2
1 3
2 4
2 5
3 6
6 7
8 9
8 10
*/