重心-[Codeforces709E] Centroids

题目就是给出一颗树,对于每一个点,如果改变树的一条边(可以不变),是否能让这个点变成重心;
zzh学长教我时,告诉我这是贪心(其实好像是树形dp),因为重心的最大的子树大小不超过全树的一半,所以贪心也是方便的,即尽可能让某个点的最大的子树大小不超过全树的一半;所谓改变一条边,显然是在某点的最大的子树里砍掉一边,添加到别的地方,这样可以使整棵树相对于这个点更平衡;
但是我们要删掉哪一条边,添加到哪里,这就要贪心了;
设询问某点x
我们知道,原先的树也是有重心的,设其是y;显然,y一定在x的最大的子树里;如果我们要建立一个新的重心x,就一定要破坏原先的重心y,很显然,改变的最优方法必然是在原重心y附近截取一个子树移到x上;
显然的,割掉原先y的最大子树接到x上是比较好的,因为y的最大子树一定不超过全树的一半,且去掉y的最大子接到x上,重心一定会从y向x移;感觉就是这样,把y的最大子树移动到x上,最优;
但本蒟蒻无法证明~~~;
当然咯,如果x恰好就在y的最大子树上,那就尴尬了,要判断两种较优情况
1.取y的次大子树接到x上;
2.把割掉y的最大子树后的那颗树接到x上;
就是这样,感觉这个贪心蒙出来的,还是dp靠谱
如果原来有好多重心,任意

#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[800009];
int head[400010],v[400005],vv[400005];//链表存,v[i]是i节点的最大子树,vv[i]是次大 
bool b[400005];//标记点i是否在原重心的最大子树上 
int n,m,x,y,ans,ll,maxc,z;
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){
    int k=head[x],an1=0,an=0,c=0,ann=0;
    int sum=1;
    while(k){
        if(a[k].to!=y)an=dfs(a[k].to,x);else an=0;
        sum+=an;
        if(an>an1){
            if(an1>ann)ann=an1;
            an1=an;c=a[k].to;
        }else if(an>ann)ann=an;
        k=a[k].next;
    }
    an=n-sum;
    if(an>an1){
        if(an1>ann)ann=an1;
        an1=an;c=y;
    }else if(an>ann)ann=an;
    v[x]=an1;vv[x]=ann;
    if(an1<=n/2){
        ans=x;maxc=c;
    }
    return sum;
}
int dfs2(int x,int y){
    int k=head[x];
    while(k){
        if(a[k].to!=y){
            b[a[k].to]=1;
            dfs2(a[k].to,x);
        }
        k=a[k].next;
    }
}
int main()
{
        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=0,c=0,ann=0;
    while(k){
        an=dfs(a[k].to,1);
        if(an>an1){
            if(an1>ann)ann=an1;
            an1=an;c=a[k].to;
        }else if(an>ann)ann=an;
        k=a[k].next;
    }
    v[1]=an1; vv[1]=ann;
    if(an1<=n/2){
        ans=1;maxc=c;
    }
    b[maxc]=1;
    dfs2(maxc,ans);
    for(int i=1;i<=n;i++){
        if(b[i]){
            if(max(v[i]-vv[ans],vv[ans])<=n/2){printf("1 ");continue;}
            if(n-v[ans]<=n/2){printf("1 ");continue;}
            printf("0 ");
        }else{
            if(max(v[ans],v[i]-v[ans])<=n/2){printf("1 ");continue;}
            printf("0 ");
        }
    }
}
posted @ 2017-01-21 20:50  largecube233  阅读(203)  评论(0编辑  收藏  举报