708C:Centroids
题意简述
给你一棵
n
个结点的树,你有一次机会把一条边断掉然后在任意位置接上一条边,但要保证操作之后还是一棵树。
现在求每个点进行这种操作之后是否可能成为重心,即所有子树大小都不超过
数据范围
1≤n≤4∗105
思路
考虑某一个结点
x
可能成为重心的条件。
注意到一个重要的性质,一个结点
那么肯定是
x
的那个孩子的子树中有一条边被切了,并且两边
那么树形DP。
设
son[i]
为结点
i
的
设
f[i]
为结点
i
的子树中,以某一节点为根的子树的
设
pos[i]
为
f[i]
的取值在
i
的哪一个孩子的子树中。
设
设
转移什么的看代码吧…
特别麻烦= =
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct edge{
int s,t,next;
}e[800010];
int head[400010],cnt;
void addedge(int s,int t)
{
e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
int n,u,v,siz;
int son[400010],size[400010],f[400010],se[400010],pos[400010],fa[400010],g[400010];
bool ok[400010];
void dfs(int node,int lastfa)
{
fa[node]=lastfa;
size[node]=1;
son[node]=0;
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
dfs(e[i].t,node);
size[node]+=size[e[i].t];
if (size[e[i].t]>size[son[node]])
son[node]=e[i].t;
if (size[e[i].t]<=n/2)
{
if (size[e[i].t]>f[node])
{
se[node]=f[node];
f[node]=size[e[i].t];
pos[node]=e[i].t;
}
else if (size[e[i].t]>se[node])
se[node]=size[e[i].t];
}
else if (f[e[i].t]>f[node])
{
se[node]=f[node];
f[node]=f[e[i].t];
pos[node]=e[i].t;
}
else if (f[e[i].t]>se[node])
se[node]=f[e[i].t];
}
if (n-size[node]>size[son[node]])
son[node]=lastfa;
}
void dfs2(int node,int lastfa)
{
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
if (n-size[e[i].t]<=n/2)
g[e[i].t]=max(g[e[i].t],n-size[e[i].t]);
g[e[i].t]=max(g[e[i].t],g[node]);
if (pos[node]==e[i].t)
g[e[i].t]=max(g[e[i].t],se[node]);
else
g[e[i].t]=max(g[e[i].t],f[node]);
dfs2(e[i].t,node);
}
}
int main()
{
scanf("%d",&n);
memset(head,0xff,sizeof(head));
cnt=0;
for (int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
}
dfs(1,1);
dfs2(1,1);
for (int i=1;i<=n;i++)
if (son[i]==fa[i])
ok[i]=(n-size[i]-g[i]<=n/2);
else
ok[i]=(size[son[i]]-f[i]<=n/2);
for (int i=1;i<=n;i++)
printf("%d ",ok[i]);
return 0;
}