标签:add span 个数 连通块 最大的 题目 ons 连通 using
题目描述
我们定义一个点\(x\)是整棵树的重心(总共有\(n\)个点)当且仅当删掉点x后所有连通块的大小都不超过\(n/2\)。我们定义一次操作为删掉一条边再加上一条边,必须满足操作后整个图还是一棵树。给你一棵\(n\)个点的树,求每个点能否在一次操作后成为重心。
输入
第一行一个整数\(n\);
接下来\(n-1\)行每行两个整数\(u\),\(v\):表示第\(u\)个点与第\(v\)个点间有一条边。
输出
共\(1\)行\(n\)个数:第\(i\)个数表示第\(i\)个点能否在一次操作后成为重心。(若可以则输出\(1\),若不可以则输出\(0\))
样例输入
3
1 2
2 3
样例输出
1 1 1
样例解释
样例1:我们可以删掉边\((2,3)\),然后加上边\((1,3)\),这样第一个点就可以成为重心。
数据范围
\(2?≤?n?≤?400?000\)
\(1≤u_i,v_i≤n\)
如果这个树本来就是重心,那么肯定可以成为重心。
如果一个树不是重心,那么一定有一颗子树它的重量\(≥n/2\),我们当然要把这个子树拆掉一部分。
拆掉之后装在哪里呢,当然就是判断当前的点上啊。
对于当前拆掉的那一部分,我们肯定要求它\(≤n/2\)不然还不是重心。
于是我们有\(f[x]\)表示\(f[x]\)的子树中满足\(≤n/2\)的最大的子树。
对于一个点,我们在判断的时候判一下把子树移出来是否满足即可。
当然他父亲的的子树的信息要第二次\(dfs\)时候处理。
如果当前处理的子树正好是他父亲的\(f\)值所在的子树,我们当然要取父亲的次小值。
于是对于每个点,我们只要存下\(f\),次小\(f\),\(siz\)。
#include<bits/stdc++.h>
using namespace std;
const int N=4e6+10;
int n, cnt, head[N], siz[N];
int maxson[N], f[N][2], h[N], g[N], ans[N];
struct EDGE {
int y, nex;
} edge[N<<1];
void add(int x, int y) {
edge[++cnt].y=y;
edge[cnt].nex=head[x];
head[x]=cnt++;
}
void dfs1(int x, int fa) {
siz[x]=1;
for(int i=head[x]; i; i=edge[i].nex) {
int y=edge[i].y;
if(y==fa) continue;
dfs1(y, x);
siz[x]+=siz[y];
if(siz[y]>siz[maxson[x]]) maxson[x]=y;
int cur = (siz[y] <= n / 2) ? siz[y] : f[y][0];
if(cur > f[x][0]) {
f[x][1] = f[x][0];
f[x][0] = cur;
g[x] = y;
} else if(cur > f[x][1])
f[x][1] = cur;
}
}
void dfs2(int x, int fa) {
ans[x]=true;
if(siz[maxson[x]]>n/2)
ans[x] = (siz[maxson[x]]-f[maxson[x]][0]<=n/2);
else if(n-siz[x]>n/2)
ans[x]=(n-siz[x]-h[x]<=n/2);
for(int i=head[x]; i; i=edge[i].nex) {
int y=edge[i].y;
if(y==fa) continue;
int cur=(n-siz[x]<=n/2)?(n-siz[x]):h[x];
h[y]=max(h[y], cur);
h[y]=max(h[y], f[x][g[x]==y]);
dfs2(y, x);
}
}
int main() {
scanf("%d", &n);
for(int i=1; i<n; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs1(1, 0), dfs2(1, 0);
for(int i=1; i<=n; i++) printf("%d ", ans[i]);
}
【XSY2407】【CF708C】Centroids 树形DP
标签:add span 个数 连通块 最大的 题目 ons 连通 using
原文地址:https://www.cnblogs.com/2017gdgzoi1164/p/14613201.html