HDU 6567 Cotree (树的重心)2019 ccpc江西省赛 A题

题目大意:两个树,连一条边使得最小

题目思路:

       根据树的重心的性质:

             树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。

我们只要找出两棵树中,每棵树的所有点到一个点的距离和最小就可以了。

也就是找重心连接起来。

           然后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
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值