T
h
e
C
o
w
G
a
t
h
e
r
i
n
g
The\ Cow\ Gathering
The Cow Gathering
D
e
s
c
r
i
p
t
i
o
n
\mathcal{Description}
Description
给定
N
N
N 个节点的树, 有
M
M
M 条限制条件 :
a
i
a_i
ai 比
b
i
b_i
bi 先删除.
请输出 所有节点能否最后被删除.
S
o
l
u
t
i
o
n
1
\mathcal{Solution_1}
Solution1
步
骤
:
\color{red}{步骤:}
步骤:
- 找到任意一个满足条件的点 r o o t root root
- 统计答案
找
r
o
o
t
:
\color{blue}{找\ root:}
找 root:
- a i a_i ai 向 b i b_i bi 连有向边
- 拓 扑 排 序 \color{black}{拓扑排序} 拓扑排序
- 若存在 环 环 环, 说明无解,
- 否则取出 拓 扑 序 最 大 的 节 点 \color{purple}{拓扑序最大的节点} 拓扑序最大的节点, 即为一个满足条件的节点 r o o t root root.
统
计
答
案
:
\color{blue}{统计答案:}
统计答案:
以
r
o
o
t
root
root 为根建树 :
对于一颗子树, 设该节点为 s s s,
分 两 种 情 况 讨 论 : 分两种情况讨论: 分两种情况讨论:
-
s \color{red}{s} s 有连出去的 有向边.
以 s \color{red}{s} s 为根节点的子树所有节点全部不能作为最后一个节点出去
理 由 : 不 明 \color{purple}理由: 不明 理由:不明
-
s \color{red}{s} s 没有连出去的 有向边 .
不操作
S
o
l
u
t
i
o
n
2
\mathcal{Solution_2}
Solution2
使用 树上差分, 以 d f n [ ] dfn[] dfn[] 为下标建立 差 分 数 组 差分数组 差分数组,
对于 a i , b i a_i, b_i ai,bi, 同样 a i a_i ai 向 b i b_i bi 连一条有向边,
先使用 拓 扑 排 序 拓扑排序 拓扑排序 判环, 若有环说明 无解, 否则 :
此时分为三种情况 :
- a a a 在 b b b 上方
- b b b 在 a a a 上方
- a a a 和 b b b 拥有 最近公共祖先
对于
情
况
2
情况2
情况2 和
情
况
3
情况3
情况3, 直接将
d
f
n
[
a
]
,
d
f
n
[
a
]
+
s
i
z
e
[
a
]
dfn[a], \ dfn[a]+size[a]
dfn[a], dfn[a]+size[a] 进行差分操作,
对于
情
况
1
情况1
情况1, 记 全局数组
f
l
a
g
flag
flag, 将
f
l
a
g
+
+
flag++
flag++,
b
b
b 跳到
a
a
a 的正下方后 !!
d f n [ b ] , d f n [ b ] + 1 dfn[b],\ dfn[b]+1 dfn[b], dfn[b]+1 进行 反差分操作.
最后根据 差 分 数 组 差分数组 差分数组 统计答案.
C
o
d
e
\mathcal{Code}
Code
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 100005;
int N;
int M;
int num0;
int tot;
int flag;
int C[maxn];
int rd[maxn];
int dfn[maxn];
int dep[maxn];
int size[maxn];
int head[maxn];
int Fk[maxn][20];
bool Used[maxn];
struct Edge{ int nxt, to; } edge[maxn<<2];
void Add(int from, int to){
edge[++ num0] = (Edge){ head[from], to };
head[from] = num0;
}
void DFS(int k, int fa){
dfn[k] = ++ tot;
dep[k] = dep[fa] + 1;
size[k] = 1;
for(reg int i = 1; i <= 19; i ++) Fk[k][i] = Fk[Fk[k][i-1]][i-1];
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
Fk[to][0] = k;
DFS(to, k);
size[k] += size[to];
}
}
int LCA(int a, int b){
if(dep[a] < dep[b]) std::swap(a, b);
for(reg int i = 19; i >= 0; i --)
if(dep[Fk[a][i]] >= dep[b]) a = Fk[a][i];
if(a == b) return a;
for(reg int i = 19; i >= 0; i --)
if(Fk[a][i] != Fk[b][i]) a = Fk[a][i], b = Fk[b][i];
return Fk[a][0];
}
bool chk(){
tot = 0;
std::queue <int> Q;
for(int i = 1; i <= N; i ++)
if(rd[i] == 1) Q.push(i), rd[i] --;
while(!Q.empty()){
int ft = Q.front(); Q.pop();
tot ++, rd[ft] --;
for(reg int i = head[ft]; i; i = edge[i].nxt){
int to = edge[i].to;
if(-- rd[to] == 1) Q.push(to);
}
}
if(tot < N) return 0;
return 1;
}
int jump(int a, int b){
for(reg int i = 19; i >= 0; i --)
if(dep[Fk[a][i]] > dep[b]) a = Fk[a][i];
return a;
}
int main(){
freopen("visit.in", "r", stdin);
freopen("visit.out", "w", stdout);
N = read();
M = read();
for(reg int i = 1; i < N; i ++){
int x = read(), y = read();
Add(x, y), Add(y, x);
rd[x] ++, rd[y] ++;
}
DFS(1, 0);
for(reg int i = 1; i <= M; i ++){
int a = read(), b = read();
Add(a, b), rd[b] ++;
int lca = LCA(a, b);
if(lca == a){ // a up b
int t = jump(b, a);
C[dfn[t]] --, C[dfn[t]+size[t]] ++;
flag ++;
}else if(lca != a && lca != b){ // a ^ b
C[dfn[a]] ++;
C[dfn[a]+size[a]] --;
}else if(lca == b){ // b up a
C[dfn[a]] ++;
C[dfn[a]+size[a]] --;
}
}
if(!chk()){
for(reg int i = 1; i <= N; i ++) printf("0\n");
return 0;
}else{
for(reg int i = 1; i <= N; i ++) C[i] += C[i-1];
for(reg int i = 1; i <= N; i ++)
if(C[dfn[i]]+flag > 0) printf("0\n");
else printf("1\n");
}
return 0;
}