n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
0<n,m≤2∗104和0<n,m≤2∗105
其实就是要打一个可持久化数组。
其实就是要打一个可持久化线段树QAQ
其实一直很想学来着QAQ
以前是打过动态开点线段树。
现在想想其实有些相似诶
合并集合的时候好像可以启发式合并诶
是按集合的轶合并的QAQ
大概是这样的啦
void Modify(int &o, int x, int l, int r, int pos, int k) {
o = ++Tcnt;
if (l == r) {
fa[o] = k; Rank[o] = Rank[x];
return;
}
ls[o] = ls[x]; rs[o] = rs[x];
int mid = (l + r) >> 1;
if (pos <= mid) Modify(ls[o], ls[x], l, mid, pos, k);
else Modify(rs[o], rs[x], mid + 1, r, pos, k);
}
if (Rank[p1] > Rank[p2]) swap(p1, p2);
Modify(rt[i], rt[i - 1], 1, n, fa[p1], fa[p2]);
if (Rank[p1] == Rank[p2]) Add(rt[i], 1, n, fa[p2]);
终于算打过可持久化线段树了QAQ
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
const int N = 10000100;
inline char get(void) {
static char buf[1000000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(int &x) {
x = 0; char c = get(); int sign = 1;
for (; c < '0' || c > '9'; c = get()) if(c == '-') sign = 0;
for (; c >= '0' && c <= '9'; x = (x << 1) + (x << 3) + c - '0', c = get());
x = sign ? x : -x;
}
int rt[200010], ls[N], rs[N], fa[N], Rank[N];
int n, m, x, y, opr, Tcnt, p1, p2, last;
void Build(int &o, int l, int r) {
if (!o) o = ++Tcnt;
if (l == r) {
fa[o] = l; return;
}
int mid = (l + r) >> 1;
Build(ls[o], l, mid);
Build(rs[o], mid + 1, r);
}
int Query(int o, int l, int r, int pos) {
if (l == r) return o;
int mid = (l + r) >> 1;
if (pos <= mid) return Query(ls[o], l, mid, pos);
else return Query(rs[o], mid + 1, r, pos);
}
void Modify(int &o, int x, int l, int r, int pos, int k) {
o = ++Tcnt;
if (l == r) {
fa[o] = k; Rank[o] = Rank[x];
return;
}
ls[o] = ls[x]; rs[o] = rs[x];
int mid = (l + r) >> 1;
if (pos <= mid) Modify(ls[o], ls[x], l, mid, pos, k);
else Modify(rs[o], rs[x], mid + 1, r, pos, k);
}
void Add(int o, int l, int r, int pos) {
if (l == r) {
Rank[o]++; return;
}
int mid = (l + r) >> 1;
if (pos <= mid) Add(ls[o], l, mid, pos);
else Add(rs[o], mid + 1, r, pos);
}
int find(int rt, int x) {
int Pos = Query(rt, 1, n, x);
if (x == fa[Pos]) return Pos;
return find(rt, fa[Pos]);
}
int main(void) {
freopen("1.in", "r", stdin);
read(n); read(m);
Build(rt[0], 1, n);
for (int i = 1; i <= m; i++) {
read(opr);
if (opr == 1) {
read(x); read(y); rt[i] = rt[i - 1];
x ^= last; y ^= last;
p1 = find(rt[i], x); p2 = find(rt[i], y);
if (fa[p1] == fa[p2]) continue;
if (Rank[p1] > Rank[p2]) swap(p1, p2);
Modify(rt[i], rt[i - 1], 1, n, fa[p1], fa[p2]);
if (Rank[p1] == Rank[p2]) Add(rt[i], 1, n, fa[p2]);
} else if (opr == 2) {
read(x); x ^= last; rt[i] = rt[x];
} else {
read(x); read(y); rt[i] = rt[i - 1];
x ^= last; y ^= last;
p1 = find(rt[i], x); p2 = find(rt[i], y);
if (fa[p1] == fa[p2]) {
puts("1"); last = 1;
} else {
puts("0"); last = 0;
}
}
}
return 0;
}

本文介绍了一种数据结构——可持久化线段树的实现方法,并通过一个具体的集合合并问题实例展示了其应用过程。文章详细解释了如何通过启发式合并等技巧优化集合操作,同时提供了完整的代码实现。
4482

被折叠的 条评论
为什么被折叠?



