【CF 708C】Centroids(树型DP)

来源:互联网 发布:航空票务网站源码 编辑:程序博客网 时间:2024/06/05 23:07
C. Centroids
time limit per test
4 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Tree is a connected acyclic graph. Suppose you are given a tree consisting of n vertices. The vertex of this tree is called centroid if the size of each connected component that appears if this vertex is removed from the tree doesn’t exceed .

You are given a tree of size n and can perform no more than one edge replacement. Edge replacement is the operation of removing one edge from the tree (without deleting incident vertices) and inserting one new edge (without adding new vertices) in such a way that the graph remains a tree. For each vertex you have to determine if it’s possible to make it centroid by performing no more than one edge replacement.

Input

The first line of the input contains an integer n (2 ≤ n ≤ 400 000) — the number of vertices in the tree. Each of the next n - 1 lines contains a pair of vertex indices ui and vi (1 ≤ ui, vi ≤ n) — endpoints of the corresponding edge.

Output

Print n integers. The i-th of them should be equal to 1 if the i-th vertex can be made centroid by replacing no more than one edge, and should be equal to 0 otherwise.

Examples
Input
3
1 2
2 3
Output
1 1 1 
Input
5
1 2
1 3
1 4
1 5
Output
1 0 0 0 0 
Note

In the first sample each vertex can be made a centroid. For example, in order to turn vertex 1 to centroid one have to replace the edge (2, 3) with the edge (1, 3).

赛时感觉是个树DP,剩20分钟,就没敲,去搞分了……今天按思路想想顺着就出来了。。。=。= (反正加上这分也是要掉……

题目大意:
给定一个树上操作:删掉一个树边,此时会变成两棵树,然后把其中一棵的断点接到另一棵树的任何一个节点上。
问对于树上每一个节点,能否通过最多一次改操作,把该点变为树的重心。

对于一个节点,与其相连的子树:
1节点数均 < n/2:那么不用操作它就是重心。
2
有超过1个子树节点数 > n/2:一次操作不足把它变成重心(需要把 > n/2的子树都截短
3`有且仅有1个子树节点数 > n/2:只要保证 (这颗子树的节点数-该子树中最大的 < n/2的子树的节点数) < n/2 即可。(也就是从这颗子树中切下最大的一个不超n/2的子树,剩余的节点数也不超n/2,那么就可以把剪下的树接到当前查询的节点上。查询点就变成重心了

理论结束,开始敲。
先dfs一遍,把每个节点u为根的子树大小sub跑出来,然后跑出来u为根的子树中不超n/2的最大的子树maxsub大小。

第二遍就可以搜答案了,dfs的过程中,统计一下当前节点u相连的子树中节点数 > n/2的子树个数。0或者 > 1的话直接标记答案即可。

如果u只有一棵 > n/2的子树:
1该子树为u的儿子,直接比较第一遍dfs出的sub-maxsub <= n/2
2
该子树为u的父亲(这里就相当于把u变成了根),看一下把u这颗子树抛除后子树大小(也就是n-sub[u])与最大的不超过n/2的子树做差,和n/2比较一下。
这里找最大的不超过n/2的子树,需要dfs的时候进行传递,用最大次大之类的,都是套路了。

代码如下:

#include <iostream>#include <algorithm>#include <cstring>#include <cstdlib>#include <cstdio>#include <queue>#include <stack>#include <vector>#include <map>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)#define Pr pair<int,int>#define VI vector<int>#define LL long longusing namespace std;const int INF = 0x3f3f3f3f;const int mod = 1e9+7;const double eps = 1e-8;struct Edge{    int v,next;};Edge eg[888888];int head[444444];int ans[444444];int tp,fuc,n;int sub[444444],submx[444444];void Add(int u,int v){    eg[tp].v = v;    eg[tp].next = head[u];    head[u] = tp++;}void dfs(int u,int pre){    sub[u] = 1;    submx[u] = 1;    int v;    int tmp = 1;    for(int i = head[u]; i != -1; i = eg[i].next)    {        v = eg[i].v;        if(v == pre) continue;        dfs(v,u);        sub[u] += sub[v];        submx[u] = max(submx[u],submx[v]);        tmp += sub[v];    }    if(tmp <= fuc) submx[u] = tmp;}void dfs2(int u,int pre,int subfa,int mxfa){    //printf("%d %d %d\n",u,subfa,mxfa);    int cnt = subfa > fuc;    int id,v;    if(cnt) id = -1;    int tmp = n;    int mx1,mx2;    int id1,id2;    mx1 = mx2 = -1;    for(int i = head[u]; i != -1; i = eg[i].next)    {        v = eg[i].v;        if(v == pre) continue;        if(id1 == -1 || mx1 < submx[v])        {            mx2 = mx1;            id2 = id1;            mx1 = submx[v];            id1 = v;        }        else if(id2 == -1 || mx2 < submx[v])        {            mx2 = submx[v];            id2 = v;        }    }    for(int i = head[u]; i != -1; i = eg[i].next)    {        v = eg[i].v;        if(v == pre) continue;        dfs2(v,u,sub[u]-sub[v]+subfa,max(tmp-sub[v] <= fuc? tmp-sub[v]: 0,max(mxfa,id1 == v? mx2: mx1)));        if(sub[v] > fuc)        {            cnt++;            id = v;        }    }    if(cnt > 1) ans[u] = 0;    else if(cnt == 0) ans[u] = 1;    else if(id != -1) ans[u] = (sub[id]-submx[id] <= fuc);    else ans[u] = (subfa-mxfa <= fuc);}void solve(){    fuc = n/2;    dfs(1,1);    dfs2(1,1,0,0);}int main(){    int u,v;    scanf("%d",&n);    tp = 0;    memset(head,-1,sizeof(head));    for(int i = 1; i < n; ++i)    {        scanf("%d%d",&u,&v);        Add(u,v);        Add(v,u);    }    solve();    for(int i = 1; i <= n; ++i)    {        if(i != 1) putchar(' ');        printf("%d",ans[i]);    }    return 0;}

2016-8-27修正——————————-
子树 > n/2的判断是没必要的,因为根本木可能=,=。
谢谢 @LittlePointer 指正

0 0
原创粉丝点击