链接:JXCPC A-Cotree
Avin has two trees which are not connected. He asks you to add an edge between them to make them connected while minimizing the function ∑ i = 1 n − 1 ∑ j = i + 1 n d i s t a n c e ( i , j ) \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}distance(i,j) i=1∑n−1j=i+1∑ndistance(i,j), where dis(i, j) represents the number of edges of the path from i to j. He is happy with only the function value.
Input
The first line contains a number n (2<=n<=100000). In each of the following n−2 lines, there are two numbers u and v, meaning that there is an edge between u and v. The input is guaranteed to contain exactly two trees.
Output
Just print the minimum function value.
Sample Input
3
1 2
Sample Output
4
给出两棵树(共n个结点,即n-2条边),加一条边连接两棵树使得得到的新树上结点两两距离和最小。即 ∑ i = 1 n − 1 ∑ j = i + 1 n d i s t a n c e ( i , j ) \sum_{i=1}^{n-1}\sum_{j=i+1}^{n}distance(i,j) i=1∑n−1j=i+1∑ndistance(i,j)最小。(两结点的距离即 i -> j 路径上边的数量)
树的重心的性质:
树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
所以要先求得两棵树的重心(Click here),然后连接重心后求结点的两两距离和即可。
任意一条边对距离和的贡献 = 左端结点个数 * 右端结点个数 * 边权
该题直接令边权 = 1即可。
注意在求两棵树的重心前要先得到两棵树上结点的个数。
#include
#include
#include
#include
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+50;
const int maxm=2e5+50;
struct edge
{
int u;
int v;
int next;
}e[maxm];
int head[maxn],cnt;
void addedge(int u,int v)
{
e[cnt].u=u;
e[cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
int n;
bool vis[maxn];
int tot;
void DFS0(int u)
{
if(vis[u])
return;
vis[u]=true;
tot++;
for(int i=head[u];i!=-1;i=e[i].next)
DFS0(e[i].v);
}
int p,minimum;
int DFS1(int u,int pre,int N)
{
int sum=0,max_sub=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(v==pre)
continue;
int t=DFS1(v,u,N);
max_sub=max(max_sub,t);
sum+=t;
}
max_sub=max(max_sub,N-sum-1);
if(max_sub<minimum)
{
minimum=max_sub;
p=u;
}
return sum+1;
}
LL ans;
int DFS2(int u,int pre)
{
int sum=0;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(v==pre)
continue;
int t=DFS2(v,u);
ans+=(1LL*t)*(1LL*(n-t));
sum+=t;
}
return sum+1;
}
int main()
{
memset(head,-1,sizeof(head));
cnt=0;
scanf("%d",&n);
for(int i=1;i<=n-2;i++)
{
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
int n1,n2,s1,s2;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
tot=0;
DFS0(i); //分别得到两棵树的源点s和结点个数n
if(i==1)
{
s1=i;
n1=tot;
}
else
{
s2=i;
n2=tot;
}
}
}
int g1,g2;
//得到树1的重心g1
minimum=INF;
DFS1(s1,-1,n1);
g1=p;
//得到树2的重心g2
minimum=INF;
DFS1(s2,-1,n2);
g2=p;
//连接g1、g2
addedge(g1,g2);
addedge(g2,g1);
//算出距离和
ans=0;
DFS2(1,-1);
printf("%lld\n",ans);
return 0;
}