Codeforces Round #670 (Div. 2)(树的重心,dfs求子树大小)

地址:http://codeforces.com/contest/1406/problem/C

题意:

给出n个点,n-1条边。

通过删除一条边,增加一条边,使得重心唯一

重心:树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。

解析:

关于重心的一个性质:

一棵树最多两个重心,若两个,则相邻。

那么,如果一个点x为重心,其子树点的数目一定为n/2,那么与他相邻的那个点y,其含有的点数目要么为n/2,要么为n/2+1

不管是剩n/2还是n/2+1,只要某个子树达到了n/2大小,都进行一次操作:

找出y的其中一个子节点,断掉,与x连上。

此时不管y是否为重心,都可以保证重心唯一。

而如果没有出现大小为n/2的子树,那么重心一定唯一,此时删加同一条边即可。

#include <bits/stdc++.h>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
vector<int>g[maxn];
int si[maxn];
int n;
int ok  = 0 ;
int x,y;
int dfs(int v,int f)
{
    si[v]=1;
    for(int i=0;i<g[v].size();i++)
    {
        if(f!=g[v][i])
        {
            dfs(g[v][i],v);
            si[v]+=si[g[v][i]];
        }
    }
    if(si[v]==n/2)
    {
        x=v;
        y=f;
        ok=1;
    }
}
int main(){
    int t;
    cin >> t;
    while(t--) {
        ok=0;
        scanf("%d",&n);
        for(int i=0;i<maxn;i++)
        {
            g[i].clear();
            si[i]=0;
        }
        int a,b;
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
        }
        dfs(1,0);
        if(!ok)
        {
            cout<<a<<" "<<b<<endl;
            cout<<a<<" "<<b<<endl;
        }
        else
        {
            for(int i=0;i<g[y].size();i++)
            {
                if(g[y][i]!=x)
                {
                    cout<<g[y][i]<<" "<<y<<endl;
                    cout<<g[y][i]<<" "<<x<<endl;
                    break;
                }
            }
        }
      }
}
原文地址:https://www.cnblogs.com/liyexin/p/13681521.html