Tarjan

\(Tarjan\) 缩点

有向图缩点

struct SCC {
    int n, m;
    vector<int> head, from, to, nxt;
    vector<int> weight;
    int totEdge = 1;
    
    void add(int x, int y, int z = 0) {
        ++totEdge;
        weight[totEdge] = z;
        from[totEdge] = x;
        to[totEdge] = y;
        nxt[totEdge] = head[x];
        head[x] = totEdge;
    }
    
    SCC(int n, int m) : n(n), m(m) {
        head.assign(n + 5, 0);
        from.assign(m * 2 + 5, 0);
        to.assign(m * 2 + 5, 0);
        nxt.assign(m * 2 + 5, 0);
        weight.assign(m * 2 + 5, 0);
        
        init();
    }
    
    vector<int> dfn, low;
    vector<int> stk, instk;
    vector<int> scc, size;
    int tot;  // count of dfn
    int cnt;  // count of scc
    
    void tarjan(int u) {
        dfn[u] = low[u] = ++tot;
        stk.push_back(u);
        instk[u] = 1;
        for (int e = head[u]; e; e = nxt[e]) {
            int v = to[e];
            if (!dfn[v]) {
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if (instk[v]) {
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (dfn[u] == low[u]) {
            int v;
            ++cnt;
            do {
                v = stk.back();
                stk.pop_back();
                instk[v] = 0;
                scc[v] = cnt;
                size[cnt]++;
            } while (u != v);
        }
    }
    
    // extension function
    int operator[](int idx) {
        return this->scc[idx];
    }
    
    
    void init() {
        totEdge = 1;
        for (int i = 1; i <= n; i++) head[i] = 0;
    }
    
    void work() {
        dfn.assign(n + 1, 0);
        low.assign(n + 1, 0);
        instk.assign(n + 1, 0);
        scc.assign(n + 1, 0);
        size.assign(n + 1, 0);
        tot = cnt = 0;
        stk.reserve(n + 1);
		
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) tarjan(i);
        }
    }
};

使用方法(例题):

void solve() {
    int n, m;
    read(n, m);
    // 初始化,n为点数,m为边数
    SCC scc(n, m);
    for (int i = 0, u, v; i < m; i++) {
        read(u, v);
        // 加边
        scc.add(u, v);
    }
    // 获取强连通分量
    scc.work();
    
    // scc.cnt 强联通分量的个数
    // scc.size[] 对应强连通分量的元素个数,下标为强连通分量的编号
    // scc[] 获取某个点的强连通分量编号

    int ans = 0;
    for (int i = 1; i <= scc.cnt; i++) {
        ans += scc.size[i] > 1;
    }

    cout << ans << '\n';
}

无向图缩点

struct Bridge {
    int n, m;
    vector<int> head, from, to, nxt;
    vector<int> weight;
    int totEdge = 1;
    
    void add(int x, int y, int z = 0) {
        ++totEdge;
        weight[totEdge] = z;
        from[totEdge] = x;
        to[totEdge] = y;
        nxt[totEdge] = head[x];
        head[x] = totEdge;
    }
    
    Bridge(int n, int m) : n(n), m(m) {
        head.assign(n + 5, 0);
        from.assign(m * 2 + 5, 0);
        to.assign(m * 2 + 5, 0);
        nxt.assign(m * 2 + 5, 0);
        weight.assign(m * 2 + 5, 0);
        
        init();
    }
    
    vector<int> dfn, low;
    vector<bool> bridge;
    vector<int> dcc, size;
    int tot;  // count of dfn
    int cnt;  // count of dcc
    
    void tarjan(int u, int in_edge) {
        dfn[u] = low[u] = ++tot;
        for (int e = head[u]; e; e = nxt[e]) {
            int v = to[e];
            if (!dfn[v]) {
                tarjan(v, e);
                low[u] = min(low[u], low[v]);
                if (low[v] > low[u]) {
                    bridge[e] = bridge[e ^ 1] = true;
                }
            }
            else if (e != (in_edge ^ 1)) low[u] = min(low[u], dfn[v]);
        }
    }
    
    void dfs(int u) {
        dcc[u] = cnt;
        ++size[u];
        for (int e = head[u]; e; e = nxt[e]) {
            int v = to[e];
            if (dcc[v] || bridge[e]) continue;
            dfs(v);
        }
    }
    
    bool operator[](int idx) const {
        return this->dcc[idx];
    }
    
    void init() {
        totEdge = 1;
        for (int i = 1; i <= n; i++) head[i] = 0;
    }
    
    void work() {
        dfn.assign(n + 1, 0);
        low.assign(n + 1, 0);
        bridge.assign(totEdge + 1, 0);
        dcc.assign(n + 1, 0);
        size.assign(n + 1, 0);
        tot = cnt = 0;
        
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) tarjan(i, 0);
        }
        for (int i = 1; i <= n; i++) {
            if (!dcc[i]) {
                ++cnt;
                dfs(i);
            }
        }
    }
};

使用方法 例题

void solve() {
    int n, m;
    read(n, m);
    // 初始化,n为点数,m为边数
    Bridge scc(n, m);
    for (int i = 0, u, v; i < m; i++) {
        read(u, v);
        // 加边
        scc.add(u, v);
    }
    // 获取强连通分量
    scc.work();
    
    // scc.cnt 强联通分量的个数
    // scc.size[] 对应强连通分量的元素个数,下标为强连通分量的编号
    // scc[] 获取某个点的强连通分量编号
    // scc.bridge[] 某个边是否为桥

    int ans = 0;
    for (int i = 1; i <= scc.cnt; i++) {
        ans += scc.size[i] > 1;
    }

    cout << ans << '\n';
}
posted @ 2024-08-29 18:14  grape_king  阅读(20)  评论(0)    收藏  举报