题目分析:
因为要对每个点都进行求解,所以可以考虑换根 \(dp\)。
也就是我们先想想若给定根,怎么求解,我们发现点 \(u\) 若可以走到某一个黑色点,当且仅当它的某一个儿子可以走到这个黑色点且它可以走到它的儿子,而他能走到它的某一个儿子节点并经过儿子节点继续走,当且仅当它这个儿子的子树内有大于等于 \(2\) 个黑色点,这样就可以交替着走。
当然有一种特例就是它的某一个儿子就是黑色节点,这样直接走过去就好了。
那么直接 \(dfs\) 一遍就可以得到当前点的答案了。
这个题换根是容易的,因为只需要维护一个黑色节点数量和答案,就很简单了。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5+5;
const int MAXM = 1e6+5;
struct edge{
int nxt,to;
edge(){}
edge(int _nxt,int _to){
nxt = _nxt,to = _to;
}
}e[MAXM];
int cnt,head[MAXN],sz[MAXN],a[MAXN];
bool ans[MAXN];
void add_edge(int from,int to){
e[++cnt] = edge(head[from],to);
head[from] = cnt;
}
void dfs1(int now,int fa){
if(a[now]) ans[now] = true,ans[fa] = true;
sz[now] = a[now]; //这里的 sz 是指的黑点的数量
for(int i = head[now];i ;i = e[i].nxt){
int to = e[i].to;
if(to == fa) continue;
dfs1(to,now);
sz[now] += sz[to];
if(a[now]) ans[to] = true;
if(ans[to] && sz[to] >= 2) ans[now] = true;
}
}
void dfs2(int now,int fa){
for(int i = head[now]; i; i = e[i].nxt){
int to = e[i].to;
if(to == fa) continue;
if(sz[1] - sz[to] >= 2 && ans[now]) //这里不是很严谨,但是如果 now 可以经过 to 到达某一个黑点,那么显然 to 也可以,所以就没事了
ans[to] = true;
dfs2(to,now);
}
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int n;
cin>>n;
for(int i=1; i<=n; i++){
cin>>a[i];
}
for(int i=1; i<n; i++){
int from,to;
cin>>from>>to;
add_edge(from,to);
add_edge(to,from);
}
dfs1(1,0); //第一遍 dp
dfs2(1,0); //换根
for(int i=1; i<=n; i++){
printf("%d ",ans[i]);
}
return 0;
}