可撤销并查集
之前没有写过可撤销并查集,这里整理一下.
普通并查集是不支持撤销/断边操作的.
但是如果加边顺序是 $(1,2,3,4,5)$, 断边顺序是 $(5,4,3,2,1)$ 的话是可以维护的.
我们只需要用一个启发式合并的并查集加上栈来存储合并信息即可.
这样做可行,是因为始终是向上合并,并且删除时是从上向下删除的.
具体代码如下:
namespace ufs {
stack<int>S;
int dis[N], fa[N], size[N];
void init() {
for(int i=1;i<N;++i) {
dis[i] = 0, fa[i] = i, size[i] = 1;
}
}
int find(int x) {
return fa[x] == x ? fa[x] : find(fa[x]);
}
int dist(int x) {
return fa[x] == x ? dis[x] : dis[x] ^ dist(fa[x]);
}
void merge(int x, int y) {
int dx = dist(x);
int dy = dist(y);
x = find(x), y = find(y);
if(size[x] > size[y]) {
swap(x, y);
swap(dx, dy);
}
// size[y] > size[x]
// 令 f[x] -> y
S.push(x);
size[y] += size[x];
fa[x] = y, dis[x] = dx ^ dy ^ 1;
}
void undo(int si) {
// 撤销.
while(S.size() > si) {
int p = S.top(); S.pop();
size[fa[p]] -= size[p];
fa[p] = p, dis[p] = 0;
}
}
};
在撤销的时候删除 $\mathrm{x}$ 对 $\mathrm{fa[x]}$ 的影响就好了,这一般是较好维护的.
1.二分图 /【模板】线段树分治
来源:luogu5787
在这道题中,如果说没有删边操作则可以直接利用加权并查集维护奇偶性.
然后有删边时间的话就利用线段树分治存边,利用加权并查集在线段树上合并和撤销即可.
#include <cstdio>
#include <vector>
#include <cstring>
#include <stack>
#include <algorithm>
#define N 200009
#define ll long long
#define pb push_back
#define ls now << 1
#define rs now << 1 | 1
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
namespace ufs {
stack<int>S;
int dis[N], fa[N], size[N];
void init() {
for(int i=1;i<N;++i) {
dis[i] = 0, fa[i] = i, size[i] = 1;
}
}
int find(int x) {
return fa[x] == x ? fa[x] : find(fa[x]);
}
int dist(int x) {
return fa[x] == x ? dis[x] : dis[x] ^ dist(fa[x]);
}
void merge(int x, int y) {
int dx = dist(x);
int dy = dist(y);
x = find(x), y = find(y);
if(size[x] > size[y]) {
swap(x, y);
swap(dx, dy);
}
// size[y] > size[x]
// 令 f[x] -> y
S.push(x);
size[y] += size[x];
fa[x] = y, dis[x] = dx ^ dy ^ 1;
}
void undo(int si) {
// 撤销.
while(S.size() > si) {
int p = S.top(); S.pop();
size[fa[p]] -= size[p];
fa[p] = p, dis[p] = 0;
}
}
};
int n, m, K;
struct oper {
int x, y;
oper(int x=0,int y=0):x(x),y(y){}
};
vector<oper>G[N << 2];
void update(int l, int r, int now, int L, int R, oper v) {
if(l >= L && r <= R) {
G[now].pb(v);
return ;
}
int mid = (l + r) >> 1;
if(L <= mid) update(l, mid, ls, L, R, v);
if(R > mid) update(mid + 1, r, rs, L, R, v);
}
void dfs(int l, int r, int now, int flag) {
int cur = ufs::S.size();
if(!flag) {
// 若还没构成二分图,则可以加边.
for(int i = 0 ; i < G[now].size() ; ++ i) {
int x = G[now][i].x;
int y = G[now][i].y;
int px = ufs::find(x);
int py = ufs::find(y);
if(px != py) {
ufs::merge(x, y);
}
else {
int dx = ufs::dist(x);
int dy = ufs::dist(y);
if(dx ^ dy) {
continue;
}
else {
flag = 1;
break;
}
}
}
}
if(l == r) {
printf("%s\n", flag ? "No" : "Yes");
return ;
}
int mid = (l + r) >> 1;
dfs(l, mid, ls, flag);
dfs(mid + 1, r, rs, flag);
ufs::undo(cur);
}
int main() {
// setIO("input");
scanf("%d%d%d",&n,&m,&K);
ufs::init();
for(int i = 1; i <= m ; ++ i) {
int x, y, l, r;
scanf("%d%d%d%d",&x, &y, &l, &r);
update(0, K - 1, 1, l, r - 1, oper(x, y));
}
// 处理完了, 可以遍历了.
dfs(0, K - 1, 1, 0);
return 0;
}

浙公网安备 33010602011771号