树的重心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 :一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。