3674: 可持久化并查集加强版
Time Limit: 15 Sec Memory Limit: 256 MB
Submit: 4051 Solved: 1503
[Submit][Status][Discuss]
Description
Description:
自从zkysb出了可持久化并查集后……
hzwer:乱写能AC,暴力踩标程
KuribohG:我不路径压缩就过了!
ndsf:暴力就可以轻松虐!
zky:……
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x = x xor lastans,lastans的初始值为0
0
题解
可持久化并查集… 其实我们发现每次合并如果按秩合并的话只会改变一个点的fa, 那么就好比主席树每次有一个点不同, 修改一条链. 那么我们就可以用主席树维护每个点的fa和每个点的rk(即按秩合并的rk). 按秩合并本身暴力爬树是log, 每次查fa也变成log, 那么就是log^2n的复杂度… 用主席树维护即可.
用路径压缩有两点不对:
1. 第i棵主席树的建立由于路径压缩会改变多个点的fa, 那么空间会增加很大一部分, 不像按秩合并只会改变一个点的fa.
2. 多次合并, 每次合并一个集合的root和一个单点, 触发find后等于无效. 这样多次操作就成了一条长链… 成一条长链后那么每次操作回到这个版本, 那么路径压缩就会很长.
#include<stdio.h>
#include<algorithm>
#define Boc register char
#define Acce register int
const int maxn = 2e5 + 5;
int n, m, ans;
inline const int read()
{
Acce x = 0;
Boc ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
struct node
{
int fa, rk;
node *ls, *rs;
}pool[maxn * 50], *root[maxn], *tail = pool;
node* build(int lf, int rg)
{
node* bt = ++ tail;
if (lf == rg)
{
bt -> fa = lf, bt -> rk = 0;
return bt;
}
int mid = (lf + rg) >> 1;
bt -> ls = build(lf, mid);
bt -> rs = build(mid + 1, rg);
return bt;
}
void modify(node* &bt, node* pre, int lf, int rg, int pos, int val)
{
bt = ++ tail;
if(lf == rg) { bt -> fa = val; return; }
int mid = (lf + rg) >> 1;
if(pos <= mid) bt -> rs = pre -> rs, modify(bt -> ls, pre -> ls, lf, mid, pos, val);
else bt -> ls = pre -> ls, modify(bt -> rs, pre -> rs, mid + 1, rg, pos, val);
}
node* query(node* bt, int lf, int rg, int pos)
{
if(lf == rg) return bt;
int mid = (lf + rg) >> 1;
if(pos <= mid) return query(bt -> ls, lf, mid, pos);
else return query(bt -> rs, mid + 1, rg, pos);
}
node* find(int u, int i)
{
node* p = query(root[i], 1, n, u);
if(u == p -> fa) return p;
return find(p -> fa, i);
}
int main()
{
n = read(), m = read();
root[0] = build(1, n);
node *p, *q;
for (Acce i = 1; i <= m; ++ i)
{
int opt = read(), u, v;
if (opt == 1)
{
u = read(), v = read();
root[i] = root[i - 1];
u ^= ans, v ^= ans;
p = find(u, i - 1), q = find(v, i - 1);
if (p -> fa == q -> fa) continue;
if (p -> rk > q -> rk) std :: swap(p, q);
modify(root[i], root[i - 1], 1, n, p -> fa, q -> fa);
if(p -> rk == q -> rk) q -> rk ++;
}
if (opt == 2) u = read(), u ^= ans, root[i] = root[u];
if (opt == 3)
{
root[i] = root[i - 1];
u = read(), v = read();
u ^= ans, v ^= ans;
p = find(u, i), q = find(v, i);
ans = (p -> fa == q -> fa);
printf("%d\n", ans);
}
}
}