并查集按秩合并(可撤销)

按秩合并

并查集做成一棵树,启发式维护树高,实现\(O(logn)\),我们可以对节点进行一些处理,维护带权

模板
经典例题:\(连接(u,v)\),查询\((u,v)\)什么时候联通的

每个点有一个权值,为该子树与外部联通的时间

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5e5+5;

int n, m, fa[N], val[N], he[N], deep[N], tim, ans;
int find(int x) {
    if(x == fa[x]) return x;
    else {
        int f = find(fa[x]);
        deep[x] = deep[fa[x]] + 1;
        return f;
    }
}
void Union(int x, int y) {
    tim++;
    x = find(x); y = find(y);
    if(x == y) return;
    if(he[x] < he[y]) swap(x, y);
    fa[y] = x;
    val[y] = tim;
    he[x] = max(he[x], he[y]+1);
}
void Find(int x, int y) {
    int fx = find(x), fy = find(y);
    ans = 0;
    if(fx != fy) cout << ans << '\n';
    else {
        while(x != y) {
            if(deep[x] < deep[y]) swap(x, y);
            ans = max(ans, val[x]); x = fa[x];
        }
        cout << ans << '\n';
    }
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false); cin.tie(); cout.tie();
    cin >> n >> m;
    for(int i=1; i<=n; i++) fa[i] = i;
    for(int i=1; i<=m; i++) {
        int c, x, y;
        cin >> c >> x >> y;
        x ^= ans; y ^= ans; //printf("hi %d %d %d\n", c, x, y);
        if(c == 0) Union(x, y);
        else Find(x, y);
    }
}

可撤销

另一大优点是可撤销,将撤销后的影响通过栈存起来实现(换父亲,高度与子树大小)

inline bool merge(int x, int y) {
    int fx = findf(x), fy = findf(y);
    if (fx == fy) return 0;
    if (size[fx] > size[fy]) std::swap(fx, fy);
    fa[fx] = fy;
    size[fy] += size[fx];
    stk[++top] = fx;
    return true;
}

模板

\(ZJOJ4769\):判二分图就是看是否有奇环,\(val[x]\)表示\(x\)与父亲的关系(\(1\)\(0\)同),注意与父亲不一定相连
\(x-y\),对应的\(fx-fy\)的关系是什么呢:

\[c[x]xor~c[y]=1,c[fx]=len[x]xor~c[x],c[fy]=len[y]xor~c[y] \]

\[c[fx]xor~c[fy]=(len[x]xor~c[x])xor~(len[y]xor~c[y])=len[x]xor~len[y]xor~1 \]

posted @ 2019-06-25 11:43  y2823774827y  阅读(1128)  评论(0编辑  收藏  举报