树的重心POJ1655 Balancing Act

题目意思,就是给你几组数据,每个数据是一棵数,让你求出数的重心的标号ans2和去掉重心后所有子树中最大那一个子树的节点种数ans3;
当然咯,重心可能不只一个,所以当有多个重心时,去ans2最小的;
Sample Input

1 共1组数据
7 共7个节点
2 6
1 2
1 4
4 5
3 7
3 1
Sample Output

1 2 重心为1,重儿子最大是2(就是指去掉节点1,剩下最大的子树中节点个数为2);

首先,重心有一个特点,重心的最大的子树不超过总节点的一半;
因为如果大于一半,在大于的那一个子树里必然有一个节点跟优;
所以我们就dfs爆搜一遍O(n)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
struct cs{
    int to,next;
}a[400009];
int head[200010];
int n,m,x,y,ans1,ans2,ans3,ll;
void inc(int x,int y){
    ll++;
    a[ll].to=y;
    a[ll].next=head[x];
    head[x]=ll;
}
int dfs(int x,int y){//y就是从哪里搜过来的 
    int k=head[x],an1=0,an;
    int sum=1;//这个1就是它本身 
    while(k){
        if(a[k].to!=y)an=dfs(a[k].to,x);else an=0;//不能重复搜索 
        sum+=an;
        if(an>an1)an1=an;
        k=a[k].next;
    }
    an=n-sum;//n-sum就是y方向上的子树节点数 
    if(an>an1)an1=an;
    if(an1<=n/2)
        if(an1==ans3)ans2=min(ans2,x);else{
            ans2=x;ans3=an1;
        }
    return sum;
}
int main()
{
    scanf("%d",&m);
    while(m--){
        memset(head,0,sizeof head);
        ll=0;
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++){
            scanf("%d%d",&x,&y);
            inc(x,y);
            inc(y,x);
        }
    int k=head[1],an1=0,an;//随便枚举一个起点 
    while(k){
        an=dfs(a[k].to,1);
        if(an>an1)an1=an; //an1就是最大的子树 
        k=a[k].next;
    }
    if(an1<=n/2)
        if(an1==ans3)ans2=min(ans2,1);else{
            ans2=1;ans3=an1;
        }
    printf("%d %d\n",ans2,ans3);
}
}

树的重心定义(另一种):以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

树的重心的性质:
性质 1 :树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离

和,他们的距离和一样。

性质 2 :把两棵树通过某一点相连得到一颗新的树,新的树的重心必然在连接原来两棵

树重心的路径上。

性质 3 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。

posted @ 2017-01-20 21:52  largecube233  阅读(111)  评论(0编辑  收藏  举报