题目
问题描述
给定一个含有n个点的树,如果以某点i为根,其所有的子树的大小(节点个数)都不大于n/2,则称这个点为该树的重心,现在你有一种操作,操作可以任意删除树中的一条边,然后再任意加入一条边,删除和加入后还要保证是一棵树,问第i个点是否可以通过这种操作变成树的重心,如果可以输出1,若不不行,输出0。
数据保证百分之八十 n<=2000, 百分之百 n<=500000
输入格式
-
第一行一个数n
-
接下来n-1行,每行两个整数x,y, 表示x和y之间有一条边
输出格式
- 一行共n个数,用空格隔开
输入/输出例子
输入
5
1 2
1 3
1 4
1 5
输出
1 0 0 0 0
提示
无
解题思路
分析
题目是让我们求每个点作为根节点时能否成为树的重心,所以我们用换根DP的做法来解这题。
要让每个子树大小都小于等于
n
/
2
n/2
n/2如果这个子树本身就可以作为重心,就不用改变。
否则,就说明一定有一子树节点个数
>
n
/
2
>n/2
>n/2
我们一定是从这个子树里面选一个子树接在当前的根上面,否则不是最优的,那么就要从该子树里面找到一个小于等于
n
/
2
n/2
n/2的最大子树。
第一遍dfs
第一遍dfs主要是初始化一些数组:
inline void dfs(int u,int fa)
{
d[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
if(edge[i].to==fa)
continue;
dfs(edge[i].to,u);
int v;
d[u]+=d[edge[i].to];
if(d[edge[i].to]>d[maxd[u]])
maxd[u]=edge[i].to;
if(d[edge[i].to]<=n/2)
v=d[edge[i].to];
else
v=f[edge[i].to][0];
if(f[u][0]<v)
f[u][1]=f[u][0],f[u][0]=v,ds[u]=edge[i].to;
else if(f[u][1]<v)
f[u][1]=v;
}
}
其中,
f[i][j]为子树内最长/次长路径
d[u]为子树大小
maxd[u]为最大子树编号
ds[u]判断是否经过i
第二遍dfs
第二遍我们就开始算答案了。
首先,若
d[maxd[u]]-f[maxd[u]][0]<=n/2
或
n-d[u]-up[u]<=n/2
这个点肯定可以成为重心。
同时,别忘了更新up[i]
up[edge[i].to]=max(up[edge[i].to],v);
if(ds[u]==edge[i].to)
up[edge[i].to]=max(up[edge[i].to],f[u][1]);
else
up[edge[i].to]=max(up[edge[i].to],f[u][0]);
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+1;
inline char gc()
{
static char now[1<<20],*S,*T;
if(T==S)
{
T=(S=now)+fread(now,1,1<<20,stdin);
if(T==S)
return EOF;
}
return *S++;
}
template <typename T>
inline void Read(T&x)
{
x=0;
char c=gc();
while(c<'0'||c>'9')
c=gc();
x=c-'0';
while((c=gc())>='0'&&c<='9')
x=x*10+c-'0';
}
template <typename T, typename... Args>
inline void Read(T&x,Args&...args)
{
Read(x);
Read(args...);
}
int t,n,head[N],cnt,f[N][2],ds[N],up[N],ans[N],d[N],maxd[N],idx,u,v;
struct fy
{
int to,w,next;
}edge[N<<1];
void add(int x,int y,int z)
{
edge[++idx].to=y,edge[idx].w=z,edge[idx].next=head[x],head[x]=idx;
}
inline void dfs(int u,int fa)
{
d[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
if(edge[i].to==fa)
continue;
dfs(edge[i].to,u);
int v;
d[u]+=d[edge[i].to];
if(d[edge[i].to]>d[maxd[u]])
maxd[u]=edge[i].to;
if(d[edge[i].to]<=n/2)
v=d[edge[i].to];
else
v=f[edge[i].to][0];
if(f[u][0]<v)
f[u][1]=f[u][0],f[u][0]=v,ds[u]=edge[i].to;
else if(f[u][1]<v)
f[u][1]=v;
}
}
inline void redfs(int u,int fa)
{
ans[u]=1;
if(d[maxd[u]]>n/2)
ans[u]=bool(d[maxd[u]]-f[maxd[u]][0]<=n/2);
else if(n-d[u]>n/2)
ans[u]=bool(n-d[u]-up[u]<=n/2);
for(int i=head[u];i;i=edge[i].next)
{
if(edge[i].to==fa)
continue;
int v;
if(n-d[u]>n/2)
v=up[u];
else
v=n-d[u];
up[edge[i].to]=max(up[edge[i].to],v);
if(ds[u]==edge[i].to)
up[edge[i].to]=max(up[edge[i].to],f[u][1]);
else
up[edge[i].to]=max(up[edge[i].to],f[u][0]);
redfs(edge[i].to,u);
}
}
signed main()
{
Read(n);
for(int i=1;i<n;i++)
Read(u,v),add(u,v,1),add(v,u,1);
dfs(1,0);
redfs(1,0);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
}