题目简述
给定一棵 \(n\) 个点的树,你可以删除一条边并增加一条边,形成一棵新树。
问每个点在进行这样的操作后,是否可能成为新树的重心。
\(1\le n\le4\cdot10^5\)
思路
要让每个子树大小都小于等于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\),如果如果这个子树本身就可以作为重心,就不用改变。
考虑如果本身不能作为重心,说明一定有且只有一个子树大小大于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\)。
我们一定是从这个子树里面选一个子树接在当前的根上面,否则不是最优的。那么就要从该子树里面找到一个小于等于 \(\left\lfloor\dfrac {n} {2}\right\rfloor\) 的最大子树,然后就可以进行判断了。
考虑如何快速求出这个值。
看上去就很像一个换根 DP。
先考虑 \(dp_u\) 为 \(u\) 子树内内该值的大小。对于他的儿子 \(v\),有如下方程:
考虑换根,那我们要得到子树外能移除最大子树的大小。
考虑 \(dp'_v\) 如何得到。考虑 \(dp_u\) 的最佳转移点,如果 \(dp_u\) 最佳转移点就是 \(v\) ,那么我们就需要得到一个点第二大的能够去除的子树大小。所以必须考虑维护两个 dp 值,\(dp_{u,0}\) 表示第一大的值,\(dp_{u,1}\) 表示第二大的值。
然后如果 \(v\) 是 \(u\) 最佳转移点,那么 \(dp'_v\) 可取值为 \(dp[u][1]\) 和 \(n-siz_u(n-siz_u<=\left\lfloor\dfrac {n} {2}\right\rfloor)\),否则可取值为 \(dp_{u,0}\) 和 \(n-siz_u(n-siz_u<=\left\lfloor\dfrac{n}{2}\right\rfloor)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
struct edge {
int to,nxt;
} e[N<<1];
int n,cnt,head[N],siz[N],maxsiz[N],f[N][2],pos[N];
inline void add(int u,int v) {
e[++cnt].nxt=head[u],head[u]=cnt,e[cnt].to=v;
}
void dfs1(int u,int fa) {
siz[u]=1;
for(int i=head[u]; i; i=e[i].nxt) {
if(e[i].to==fa) {
continue;
}
dfs1(e[i].to,u),siz[u]+=siz[e[i].to];
int v=f[e[i].to][0];
if(siz[e[i].to]>siz[maxsiz[u]]) {
maxsiz[u]=e[i].to;
}
if(siz[e[i].to]<=n/2) {
v=siz[e[i].to];
}
if(f[u][0]<v) {
f[u][1]=f[u][0],f[u][0]=v,pos[u]=e[i].to;
} else if(f[u][1]<v) {
f[u][1]=v;
}
}
}
int ans[N],dp[N];
void dfs2(int u,int fa) {
ans[u]=1;
if(siz[maxsiz[u]]>n/2) {
ans[u]=(siz[maxsiz[u]]-f[maxsiz[u]][0]<=n/2);
} else if(n-siz[u]>n/2) {
ans[u]=(n-siz[u]-dp[u]<=n/2);
}
for(int i=head[u]; i; i=e[i].nxt) {
if(e[i].to==fa) {
continue;
}
int v=n-siz[u];
if(n-siz[u]>n/2) {
v=dp[u];
}
dp[e[i].to]=max(dp[e[i].to],v);
if(pos[u]==e[i].to) {
dp[e[i].to]=max(dp[e[i].to],f[u][1]);
} else {
dp[e[i].to]=max(dp[e[i].to],f[u][0]);
}
dfs2(e[i].to,u);
}
}
int main() {
scanf("%d",&n);
for(int i=1,u,v; i<=n-1; i++) {
scanf("%d %d",&u,&v),add(u,v),add(v,u);
}
dfs1(1,0),dfs2(1,0);
for(int i=1; i<=n; i++) {
printf("%d ",ans[i]);
}
return 0;
}