2019CCPC-江西省赛 A-Cotree(树的重心)

链接: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=1n1j=i+1ndistance(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=1n1j=i+1ndistance(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;
}

你可能感兴趣的:(★水题之路,★数据结构,★搜索)