并查集按秩合并(可撤销)
按秩合并
并查集做成一棵树,启发式维护树高,实现\(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
\]