[Luogu P3402]可持久化并查集
考虑把并查集放到主席树上维护,达到可以查询过去版本的目的。
由于并查集时间复杂度是基于均摊分析的,所以不能路径压缩。此处可以使用按秩合并或者启发式合并。
所以我们在主席树上维护 \(dep\) 和 \(fa\),表示这个节点的深度和父亲(这里的节点指的是主席树上的节点,父亲指的是这个节点对应实际节点的父亲)。
然后合并的时候把 \(dep\) 小的合并到 \(dep\) 大的节点上,设 \(dep[x]<dep[y]\),即在主席树中使得 \(fa[fa[x]]=fa[y]\) 即可。
按秩合并可能会出现 \(dep[x]=dep[y]\) 的情况,此时我们令 \(dep[y]\) 的深度 \(+1\) 即可。此时我们发现新节点在 \(merge\) 时候已经建过了,所以不用再建一个新版本,减少了空间。
并查集查询只需要查找实际节点对应的主席树节点是哪个即可,然后暴力跳父亲。
代码如下:
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 200010;
inline int read()
{
int s = 0, w = 1;
ri char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * w;
}
int n, m, root[N];
struct Tree
{
int lc, rc, fa, dep;
} D[N * 40];
int cnt;
void Build(int &x, int l, int r)
{
x = ++cnt;
if (l == r)
{
D[x].fa = l;
return;
}
int mid = (l + r) / 2;
Build(D[x].lc, l, mid);
Build(D[x].rc, mid + 1, r);
}
void Merge(int pre, int &x, int l, int r, int px, int py)
{
x = ++cnt;
D[x] = D[pre];
if (l == r)
{
D[x].fa = py;
return;
}
int mid = (l + r) / 2;
if (px <= mid)
Merge(D[pre].lc, D[x].lc, l, mid, px, py);
else
Merge(D[pre].rc, D[x].rc, mid + 1, r, px, py);
}
void UpDate(int x, int l, int r, int pos)
{
if (l == r)
{
D[x].dep++;
return;
}
int mid = (l + r) / 2;
if (pos <= mid)
UpDate(D[x].lc, l, mid, pos);
else
UpDate(D[x].rc, mid + 1, r, pos);
}
int Ask(int x, int l, int r, int pos)
{
if (l == r)
return x;
int mid = (l + r) / 2;
if (pos <= mid)
return Ask(D[x].lc, l, mid, pos);
return Ask(D[x].rc, mid + 1, r, pos);
}
int Find(int x, int pos)
{
int id = Ask(x, 1, n, pos);
if (D[id].fa == pos)
return id;
return Find(x, D[id].fa);
}
signed main()
{
n = read(), m = read();
Build(root[0], 1, n);
for (ri int i = 1; i <= m; i++)
{
int opt, x, y;
opt = read(), x = read();
if (opt == 1)
{
root[i] = root[i - 1], y = read();
int fx = Find(root[i], x), fy = Find(root[i], y);
if (D[fx].fa == D[fy].fa)
continue;
if (D[fx].dep > D[fy].dep)
swap(fx, fy);
Merge(root[i - 1], root[i], 1, n, D[fx].fa, D[fy].fa);
if (D[fx].dep == D[fy].dep)
UpDate(root[i], 1, n, D[fy].fa);
}
else if (opt == 2)
root[i] = root[x];
else
{
root[i] = root[i - 1], y = read();
int fx = Find(root[i], x), fy = Find(root[i], y);
printf("%d\n", (int)(fx == fy));
}
}
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。