tarjan算法板子

  • 无向图
    • 割边、割点、点双、边双
  • 有向图
    • 强联通分量、例题

无向图

  • 概念

    • 时间戳
      \(dfn[x]\),在深度优先遍历中,按照每个节点第一次被访问的顺序,依次做整数标记

    • 追溯值
      \(low[x]\),通过非搜索边能到达的最小时间戳

  • 割边判定法则

    • 无向边\((x,y)\)是割边/桥,当且仅当存在x的一个子节点满足\(dfn[x] < low[y]\)
      删除无向边\((x,y)\)后,图断开成两个部分

    • 板子

int dfn[N], low[N], dfcnt;
bool g[M];
void tarjan(int x, int ei) {
    dfn[x] = low[x] = ++dfcnt;
    for(int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (!dfn[y]) {
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (dfn[x] < low[y]) g[i] = g[i^1] = 1;
        }
        else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);
    }
}
  • 割点判定法则

    • 若x不是根节点,则x是割点当且仅当存在一个子节点y满足\(dfn[x]\leq low[y]\)
      若x是根节点,则x是割点当且仅当存在至少两个子节点\(y_1,y_2\)满足上条件

    • 板子

int dfn[N], low[N], dfcnt, rt;
bool g[N];
void tarjan(int x) {
    dfn[x] = low[x] = ++dfcnt;
    int son = 0;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (!dfn[y]) {
            tarjan(y);
            low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                son++;
                if (x != rt || son > 1) g[x] = 1;
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}
  • 点双联通分量

    • 对于,每个点双中来说,图里是不存在割点的

    • 板子

int dfn[N], low[N], dfcnt, sta[N], top, cnt;
vector dcc[N];
bool  g[N];
void tarjan(int x, int rt) {
    dfn[x] = low[x] = ++dfcnt;
    sta[++top] = x;
    int son = 0;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (!dfn[y]) {
            tarjan(y); 
            low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                son++;
                if (x != rt || son > 1)  g[x] = 1;
                dcc[++cnt].clear();
                while (1) {
                    int z = sta[top--];
                    dcc[cnt].push_back(z);
                    if (y == z) break;
                }
                dcc[cnt].push_back(x);
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}
  • 边双联通分量

    • 对于一个边双,任意两个点都有两条不重合的路径

    • 板子

int dfn[N], low[N], dfcnt;
bool g[M];
void tarjan(int x, int ei) {
    dfn[x] = low[x] = ++dfcnt;
    for(int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (!dfn[y]) {
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (dfn[x] < low[y]) g[i] = g[i^1] = 1;
        }
        else if (i != (ei^1)) low[x] = min(low[x], dfn[y]);
    }
}
int n, m, d[N], b[N], cnt, ans;
void dfs(int x) {
    b[x] = cnt;
    for(int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (b[y] || g[i]) continue;
        dfs(y);
    }
}
int main() {
    //~~~
    for(int i = 1; i <= n; i++)
        if (!dfn[i]) tarjan(i, 0);
    for(int i = 1; i <= n; i++)
        if (!b[i]) cnt++, dfs(i);
    //~~~
    return 0;
}

有向图

  • 有向图的强联通分量

    • 在一个强联通分量中,存在x到y的路径,就存在y到x的路径

    • 板子

void tarjan(int x) {
    dfn[x] = low[x] = ++dfcnt;
    s[++top] = x;
    for(int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (!b[y]) low[x] = min(low[x], dfn[y]);
    }
    if (dfn[x] == low[x]) {
        cnt++;
        while(1) {
            int y = s[top--];
            b[y] = cnt;
            size[cnt]++;
            if (x == y) break;
        }
    }
}

例题

  • luoguP3387缩点

    • 代码
#include 
#include 
#include 
#include 
using namespace std;
const int N = 1e4+5, M = 1e5+5;
struct side { int t, next; } e[M][2];
int head[N][2], tot[2];
void add(int x, int y, int k) {
    e[++tot[k]][k].next = head[x][k]; 
    head[x][k] = tot[k];
    e[tot[k]][k].t = y;
}
int n, m, w[N], r[N], d[N], ans;
int dfn[N], low[N], dfcnt, sta[N], top, cnt, bel[N], sum[N];
void tarjan(int x) {
    dfn[x] = low[x] = ++dfcnt;
    sta[++top] = x;
    for (int i = head[x][0]; i; i = e[i][0].next) {
        int y = e[i][0].t;
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (!bel[y]) low[x] = min(low[x], dfn[y]);
    }
    if (dfn[x] == low[x]) {
        cnt++;
        while (1) {
            int y = sta[top--];
            bel[y] = cnt;
            sum[cnt] += w[y];
            if (x == y) break;
        }
    }
}
queue q;
int tuopu() {
    for (int i = 1; i <= cnt; i++)
        if (!r[i]) q.push(i), d[i] = sum[i];
    while (!q.empty()) {
        int x = q.front(); q.pop();
        for (int i = head[x][1]; i; i = e[i][1].next) {
            int y = e[i][1].t;
            d[y] = max(d[y], d[x] + sum[y]);
            if (--r[y] == 0) q.push(y);
        }
    }
    for (int i = 1; i <= cnt; i++)
        ans = max(ans, d[i]);
    return ans;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%d", &w[i]);
    for (int i = 1; i <= m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y, 0);
    }
    for (int i = 1; i <= n; i++)
        if (!dfn[i]) tarjan(i);
    for (int x = 1; x <= n; x++)
        for (int i = head[x][0]; i; i = e[i][0].next) {
            int y = e[i][0].t;
            if (bel[x] != bel[y]) 
                r[bel[y]]++, add(bel[x], bel[y], 1);
        }
    printf("%d\n", tuopu());
    return 0;
}

你可能感兴趣的:(tarjan算法板子)