tarjan

Tarjan

有向图

强连通分量

void tarjan(int u) {
    dfn[u] = low[u] = ++times;
    stk[++top] = u;instk[u] = 1;
    for (int i  = h[u]; i != -1; i = ne[i]) {
        int v = to[i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (instk[u]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        ++scc_cnt;
        while (1) {
            int v = stk[top--];
            Size[scc_cnt] ++;
            id[v] = scc_cnt;
            instk[v] = 0;
            if (v == u)break;
        }
    }
}

2-sat问题

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N = 2e6 + 9;
const int M = 2e6 + 9;
int h[N], ne[M], to[M], idx;
void add(int u, int v) {
    ne[idx] = h[u], to[idx] = v, h[u] = idx++;
}
int dfn[N], low[N], times;
int sz[N], id[N], scc_cnt;
int stk[N], instk[N], top;

void tarjan(int u) {
    dfn[u] = low[u] = times++;
    stk[++top] = u;
    instk[u]= 1;
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = to[i];
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (instk[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (low[u] == dfn[u]){
        scc_cnt++;
        while (1) {
            int v = stk[top];
            top--;
            instk[v] = 0;
            id[v] = scc_cnt;
            sz[scc_cnt] ++;
            if (v == u)break;
        }
    }
}
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);idx = 0;
    for (int i = 1; i <= m; i ++) {
        int u, x, v, y;
        scanf("%d%d%d%d", &u, &x, &v, &y);
        add(u + (x^1)*n, v + y * n);//零代表v,1代表v+n,显然
        add(v + (y^1)*n, u + x * n);
    }
    for (int i = 1; i <= 2 * n; i ++) {
        if (!dfn[i]) tarjan(i);
    }
    for (int i = 1; i <= n; i ++) {
        if (id[i] == id[i + n]) {//在同一gcc无解
            puts("IMPOSSIBLE");
            return 0;
        }
    }
    puts("POSSIBLE");
    for (int i = 1; i <= n; i ++) {
        if (id[i] > id[i  + n])printf("1 ");//得到的topo序是反序,所以,越小越靠后,选后面的,又因为i+n是1,i是0
        else printf("0 ");
    }
}

无向图

求割边(桥),求边双连通分量

void tarjan(int u, int from) {
    dfn[u] = low[u] = ++times;
    stk[++top] = u;
    bool flag = 1;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int v = to[i];
        if ((i^1) == from && flag) {flag = 0;continue;}//重边
        if (!dfn[v]) {
            tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) {
                ans++;//i和i^1是桥。
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        e_dcc++;
        while (1) {
            id[stk[top]] = e_dcc;
            if (stk[top--] == u)break;
        }
    }
}

求割点(割顶),求点双连通分量

  • 首先所求得的是一棵 \(dfs\) 树,然后在这棵树上,如果子树最低能达到的时间戳,即 \(dfn[u] \leq low[v]\) 都到不了 \(u\) 上面的祖先,并且这个点 \(u \neq father\) ,意思就是这个点 \(u\) 不是这个连通块随便找的最开始遍历的点,这样 \(u\) 是一个割点。

  • 如果 \(u\) 还是满足 \(dfn[u] \leq low[v]\) 并且 \(u\) 有两个儿子,那么 \(u\) 是一个割点。

void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++times;
    stk[++top] = u;
    int cnt = 0;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int v = to[i];
        if (!dfn[v]) {
            tarjan(v, fa), ++cnt;
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                if (u != fa)cut[u] = 1;// 如果所有点到不了u上面去,那么显然 u是割点,
                					 // 可是如果u是这个联通量第一个进来的元素,那么必然没有人会到u上面去
                					 // 所以u != fa 
                else cut[u] |= (cnt > 1); // 有两个儿子也行,所以是|=
                ++dcc;
                while (1) {
                    bl[stk[top]].push_back(dcc);//可以bl[dcc].push_back(stk[top]);
                    //主要是看自己需要啥,就咋写
                    sz[dcc]++;
                    if (stk[top--] == v)break;//这里确实有点不好理解,就是说,他这团是把当前这一团外面那一团给算上的,下次补一下图咕咕咕。
                }
                bl[u].push_back(dcc);
                sz[dcc]++;
            }
        } else
            low[u] = min(low[u], dfn[v]);
    }
}
posted @ 2021-04-08 16:36  u_yan  阅读(57)  评论(0编辑  收藏  举报