题目
问题描述
给定一个含有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]);
}
文章讲述了如何使用换根深度优先搜索(DFS)和动态规划的方法来解决给定树中某个节点是否可以通过添加或删除边变为重心的问题。通过两次DFS,计算每个节点的子树大小及相关信息,确定操作策略以达到重心条件。
184





