桥与割点 - tarjan

桥判定法则:无向边(x,y)是桥,当且仅当搜索树上 存在 x 的一个子节点y,满足

\[dfn[x] <low[y] \]

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int MAXM = 100000 * 2 + 10;
int n,m,last[MAXN],edge_tot=1,root,tarjan_cnt,dfn[MAXN],low[MAXN],ans;
bool flg[MAXN];
struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[MAXM];
inline void add(int u, int v) {
    e[++edge_tot] = Edge(u, v, last[u]);
    last[u] = edge_tot;
}
void tarjan(int x, int in_edge) {
    dfn[x] = low[x] = ++tarjan_cnt;
    int num = 0;
    for(int i=last[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(!dfn[v]) {
            tarjan(v, i);
            low[x] = min(low[x], low[v]);
            if(low[v] > dfn[x]) {
            	bridge[i] = bridge[i^1] = true;
            }
        } else if(i != in_edge ^ 1){
            low[x] = min(low[x], dfn[v]);
        }
    }
    
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++) {
        int xi, yi;
        scanf("%d%d", &xi, &yi);
        add(xi, yi);
        add(yi, xi);
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i]) {
            tarjan(i, 0);
        }
    }
    return 0;
}

割点判定法则:若x不为搜索树的根,当且仅当搜索树上存在x的一个子节点y,满足

\[dfn[x] \leq low[y] \]

特别地,若x为搜索树的根,当且仅当搜索树上存在至少两个x的子节点满足上述条件
由于是小于等于号,所以不用管重边与遍历到父节点什么的

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
const int MAXM = 100000 * 2 + 10;
int n,m,last[MAXN],edge_tot,root,tarjan_cnt,dfn[MAXN],low[MAXN],ans;
bool flg[MAXN];
struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to) : u(u), v(v), to(to) {}
}e[MAXM];
inline void add(int u, int v) {
    e[++edge_tot] = Edge(u, v, last[u]);
    last[u] = edge_tot;
}
void tarjan(int x) {
    dfn[x] = low[x] = ++tarjan_cnt;
    int num = 0;
    for(int i=last[x]; i; i=e[i].to) {
        int v = e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
            if(low[v] >= dfn[x]) {
                num++;
                if(x != root || num > 1) flg[x] = true; // 不要在这里计数, 若非要在这里计数,还要加上一个 !flg[x]
            }
        } else {
            low[x] = min(low[x], dfn[v]);
        }
    }
    
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++) {
        int xi, yi;
        scanf("%d%d", &xi, &yi);
        add(xi, yi);
        add(yi, xi);
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i]) {
            root = i;
            tarjan(i);
        }
    }
    for(int i=1; i<=n; i++) { //在这里计数最好
        if(flg[i]) ans++;
    }
    printf("%d\n", ans);
    for(int i=1; i<=n; i++) {
        if(flg[i]) printf("%d ", i);
    }
    return 0;
}
posted @ 2018-10-17 08:25  Zolrk  阅读(159)  评论(0编辑  收藏  举报