P5157 The Cow Gathering [拓扑排序/树上差分]

8 篇文章 0 订阅
6 篇文章 0 订阅

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}{步骤:} :

  1. 找到任意一个满足条件的点 r o o t root root
  2. 统计答案

找   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,

分 两 种 情 况 讨 论 : 分两种情况讨论: :

  1. s \color{red}{s} s 有连出去的 有向边.

    s \color{red}{s} s 为根节点的子树所有节点全部不能作为最后一个节点出去

    理 由 : 不 明 \color{purple}理由: 不明 :

  2. 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 连一条有向边,

先使用 拓 扑 排 序 拓扑排序 判环, 若有环说明 无解, 否则 :

此时分为三种情况 :

  1. a a a b b b 上方
  2. b b b a a a 上方
  3. 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值