重心的定义是:
找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡
(一)
树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
(二)
把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
(三)
把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
hdu6567
/*
方法:
(1)求出树一的重心g1,求出树二的重心g2;
(2)将两个树的重心相连,得到就是总路径和最短(因为在不连通之前树一内部的dis之和已经确定,
树二内部的dis之和也已经确定,现在只要两棵树相连后得到的dis之和最短就好了,
所以只要重心相连,树一上的点到树二上的点dis之和就最小)
(3)连接两颗树之后计算最小距离和
每条边的距离贡献 = 这条边左边点数和 * 这条边右边点数和
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const int inf = 1e9+19;
vector <int> vc[N];
int n;
bool vis[N] = {false};
int countNum;
void dfs0(int rt)
{
if(vis[rt] == true) return ;
countNum++;
vis[rt] = true;
int len = vc[rt].size();
for(int i=0;i<len;i++)
dfs0(vc[rt][i]);
}
//记录树的重心
int g;
int miTreesize;
int dfs1(int rt,int pre,int sizeTree)
{
int len = vc[rt].size();
int sum = 0,mxsize = 0;
for(int i=0;i<len;i++)
{
if(pre == vc[rt][i]) continue;
int tp = dfs1(vc[rt][i],rt,sizeTree);
mxsize = max(mxsize,tp);
sum += tp;
}
//这里注意是 sizeTree-1-sum ,表示自己父亲那边的子树大小
mxsize = max(mxsize,sizeTree-1-sum);
if(mxsize < miTreesize){
g = rt;
miTreesize = mxsize;
}
return sum + 1;
}
//记录功能最小值
ll ans;
int dfs2(int rt,int pre)
{
int sum = 1,len = vc[rt].size();
for(int i=0;i<len;i++){
if(vc[rt][i] == pre) continue;
int tp = dfs2(vc[rt][i],rt);
//这条边对距离的贡献
ans += (1ll*tp) * (1ll*(n-tp));
sum += tp;
}
return sum;
}
int main(void)
{
cin>>n;
for(int i=0;i<n-2;i++){
int x,y;cin>>x>>y;
vc[x].push_back(y);
vc[y].push_back(x);
}
//查找两棵树的起点,数量
int st1 = -1,st2 = -1;
int cnt1 = 0,cnt2 = 0;
for(int i=1;i<=n;i++)
if(vis[i] == false)
{
countNum = 0;
dfs0(i);
if(st1 == -1){
st1 = i;cnt1 = countNum;
}
else{
st2 = i;cnt2 = countNum;
}
}
//查找两棵树的重心
int g1,g2;
miTreesize = inf;dfs1(st1,-1,cnt1);g1 = g;
miTreesize = inf;dfs1(st2,-1,cnt2);g2 = g;
//连接两棵树的重心
vc[g1].push_back(g2);
vc[g2].push_back(g1);
//求出最小功能值
ans = 0;
dfs2(1,-1);
cout<<ans<<endl;
return 0;
}