Skip to content

动态维护边双连通分量 ​

cpp
struct DynamicBCC {
    // 成员变量
    vector<int> ecc; // 双连通分量
    vector<int> dsu; // 并查集
    vector<int> siz; // 并查集大小
    vector<int> par;  // 生成树父节点
    vector<int> lcats; // LCA时间戳
    int bridge;     // 当前桥数量
    int ts;        // LCA标记
    int ecc_count; // 双连通分量数量
    int dsu_count; // 并查集数量
    // 初始化(节点数0-based)(实际用的时候为创建n+1的size)
    DynamicBCC(){}
    DynamicBCC(int n) {init(n);}
    


    void init(int n){
        ecc.resize(n);
        dsu.resize(n);
        siz.assign(n, 1);
        iota(ecc.begin(), ecc.end(), 0);
        iota(dsu.begin(), dsu.end(), 0);
        par.assign(n, -1);
        lcats.assign(n, 0);
        bridge = 0;
        ts = 0;
        ecc_count = dsu_count = n - 1;
    }
    int find_ecc(int x){
        if(x==-1)return -1;
        return ecc[x]==x?x:ecc[x]=find_ecc(ecc[x]);
    }
    int find(int x){
        x=find_ecc(x);
        return dsu[x]==x?x:dsu[x]=find(dsu[x]);
    }
    // 辅助方法:重新设置u为树根
    void make_root(int u) {
        int root = u;
        int child = -1;
        while (u != -1) {
            int p = find_ecc(par[u]);
            par[u] = child;
            dsu[u] = root;
            child = u;
            u = p;
        }
        siz[root] = siz[child];
    }
    


    // 合并两个路径(LCA算法)
    void merge_path(int a, int b) {
        ts++;
        vector<int> path_a, path_b;
        int lca = -1;
        // 双向查找LCA
        while (lca == -1) {
            if (a != -1) {
                a = find_ecc(a);
                path_a.push_back(a);
                if (lcats[a] == ts) {
                    lca = a;
                    break;
                }
                lcats[a] = ts;
                a = par[a];
            }
            if (b != -1) {
                b = find_ecc(b);
                path_b.push_back(b);
                if (lcats[b] == ts) {
                    lca = b;
                    break;
                }
                lcats[b] = ts;
                b = par[b];
            }
        }
        set<int> s;
        for (auto v : path_a) {
            s.insert(find_ecc(v));
            ecc[v] = lca;
            if (v == lca) break;
            --bridge;
        }
        for (auto v : path_b) {
            s.insert(find_ecc(v));
            ecc[v] = lca;
            if (v == lca) break;
            --bridge;
        }
        ecc_count -= s.size() -1;
    }
    // 添加边并动态维护桥的数量
    void addEdge(int u, int v) {
        u = find_ecc(u);
        v = find_ecc(v);
        if (u == v) return;

        int fu = find(u);
        int fv = find(v);

        if (fu != fv) { // 不同连通块
            bridge++;
            if (siz[fu] > siz[fv])swap(u, v), swap(fu, fv);
            make_root(u);
            par[u] = dsu[u] = v;
            siz[fv] += siz[u];
            dsu_count--;
        } else { // 同一连通块,合并环
            merge_path(u, v);
        }
    }
    
    // 获取当前桥的总数
    int bridge_count() const { return bridge; }
    int count() const { return ecc_count; }
    int comb_count() const { return dsu_count; }
} ecc;