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

The Cow GatheringThe Cow Gathering


Descriptionmathcal{Description}

给定 NN 个节点的树, 有 MM 条限制条件 : aia_ibib_i 先删除.
请输出 所有节点能否最后被删除.


Solution1mathcal{Solution_1}

:color{red}{步骤:}

  1. 找到任意一个满足条件的点 rootroot
  2. 统计答案

 root:color{blue}{找 root:}

  • aia_ibib_i 连有向边
  • color{black}{拓扑排序}
  • 若存在 , 说明无解,
  • 否则取出 color{purple}{拓扑序最大的节点}, 即为一个满足条件的节点 rootroot.

:color{blue}{统计答案:}

rootroot 为根建树 :

对于一颗子树, 设该节点为 ss,

:分两种情况讨论:

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

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

    :color{purple}理由: 不明

  2. scolor{red}{s} 没有连出去的 有向边 .

    不操作


Solution2mathcal{Solution_2}

使用 树上差分, 以 dfn[]dfn[] 为下标建立 差分数组,

对于 ai,bia_i, b_i, 同样 aia_ibib_i 连一条有向边,

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

此时分为三种情况 :

  1. aabb 上方
  2. bbaa 上方
  3. aabb 拥有 最近公共祖先

对于 2情况23情况3, 直接将 dfn[a], dfn[a]+size[a]dfn[a], dfn[a]+size[a] 进行差分操作,
对于 1情况1, 记 全局数组 flagflag, 将 flag++flag++,

bb 跳到 aa 的正下方后 !!

dfn[b], dfn[b]+1dfn[b], dfn[b]+1 进行 反差分操作.

最后根据 差分数组 统计答案.


Codemathcal{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
");
                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
");
                        else printf("1
");
        }
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822607.html