1. 程式人生 > >可持久化並查集加強版 BZOJ 3674

可持久化並查集加強版 BZOJ 3674

log 歷史 clear 必須 new 路徑壓縮 都是 return 父節點

http://www.lydsy.com/JudgeOnline/problem.php?id=3674

3674: 可持久化並查集加強版

Time Limit: 15 Sec Memory Limit: 256 MB
Submit: 3225 Solved: 1192
[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<n,m<=2*10^5


Input

Output

Sample Input

5 6
1 1 2
3 1 2
2 1
3 0 3
2 1
3 1 2

Sample Output

1
0
1
題意很簡單,就是要你寫個可持久化並查集,建議做這題之前先去看我的上一篇博客——可持久化線段樹,以及你必須會普通版的並查集。 如何構造一個可持久化並查集呢?我們知道並查集是由數組實現的,所以只要能寫出可持久化數組,自然就能寫出可持久化並查集了。查了一些資料,可持久化數組似乎就是可持久化線段樹(這個我也不太確定),但是我們還是可以用線段樹代替一下數組實現並查集,我們只需要線段樹的葉節點去合並就可以了,實際上線段樹上面的區間都是沒什麽用的,但是又不能刪掉,因為我們還要維護root[i]。同時要註意並查集合並的時候不要路徑壓縮,因為壓縮後會影響太多點,返回歷史版本會很難,所以我們選擇人畜無害的按秩合並,同樣可以快到飛起。具體實現看代碼吧。可持久化並查集可以用pb_ds庫的rope,現成模板,我也貼一份模板在下面了,不過這個跑起來的時間是自己寫的版本的兩倍。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#define X first
#define
Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 2e5 + 10; const int INF = 0x3f3f3f3f; struct Tree { int l, r, fa; } T[maxn<<5]; int root[maxn], cnt = 0; int deep[maxn];//並查集深度 int n, m; void build(int l, int r, int &x)//先建樹,進行初始化,如果有多case記得cnt初始化為0 { x = ++cnt; if (l == r) { T[x].fa = l; deep[l] = 1; return ; } int mid = (l + r) >> 1; build(l, mid, T[x].l); build(mid + 1, r, T[x].r); } int query(int l, int r, int rt, int pos)//查詢pos在線段樹中的位置 { if (l == r) return rt; int mid = (l + r) >> 1; if (mid >= pos) return query(l, mid, T[rt].l, pos); else return query(mid + 1, r, T[rt].r, pos); } int find(int x, int cur)//並查集的找祖先 { int pos = query(1, n, root[cur], x); if (T[pos].fa != x) return find(T[pos].fa, cur); return T[pos].fa; } void update(int l, int r, int &x, int y, int pos, int fa) { T[++cnt] = T[y];//繼承上一個版本 x = cnt;//與父節點連接 if (l == r) { T[x].fa = fa;//更新fa值 return ; } int mid = (l + r) >> 1; if (mid >= pos) update(l, mid, T[x].l, T[y].l, pos, fa); else update(mid + 1, r, T[x].r, T[y].r, pos, fa); } void mix(int x, int y, int cur) { int fx = find(x, cur), fy = find(y, cur); if (deep[fx] > deep[fy]) swap(fx, fy);//按秩合並 if (fx == fy) return ; deep[fy] += deep[fx]; update(1, n, root[cur], root[cur-1], fx, fy);//在線段樹fx的位置上更新為fy } int main() { #ifdef LOCAL in(); #endif scanf("%d%d", &n, &m); build(1, n, root[0]); int cur = 0, lastans = 0; while (m--) { int op, a, b, k; scanf("%d", &op); if (op == 1) { scanf("%d%d", &a, &b); a ^= lastans; b ^= lastans; root[cur+1] = root[cur]; mix(a, b, ++cur); } else if (op == 2) { scanf("%d", &k); k ^= lastans; root[++cur] = root[k];//這裏不建議改成cur = k ,因為他可能跳到前面後又跳回去,這樣會覆蓋掉歷史版本 } else { scanf("%d%d", &a, &b); a ^= lastans; b ^= lastans; root[cur+1] = root[cur]; ++cur; lastans = (find(a, cur) == find(b, cur)); printf("%d\n", lastans); } } return 0; }

pb_ds庫的rope:

#include <bits/stdc++.h>
#include <cstdio>
#include <ext/rope>
using namespace std;
using namespace __gnu_cxx;
const int maxn = 2e5 + 10;
rope <int> *f[maxn];
rope <int> :: iterator it;
int id[maxn];
int n, m, op, a, b;
int find (int &t, int x)
{
    if (f[t]->at(x) == x) return x;
    int fa = find(t, f[t]->at(x));
    if (fa == f[t]->at(x)) return fa;
    f[t]->replace(x, fa);
    return fa;
}
void mix(int &t, int x, int y)
{
    int fx = find(t, x), fy = find(t, y);
    f[t]->replace(fy, fx);
}
int lastans = 0;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i <= n; ++i) id[i] = i;
    f[0] = new rope<int>(id, id + n + 1);
    for (int i = 1; i <= m; ++i)
    {
        f[i] = new rope <int>(*f[i-1]);
        scanf("%d", &op);
        if (op == 1)
        {
            scanf("%d%d", &a, &b);
            mix(i, a ^ lastans, b ^ lastans);
        }
        else if (op == 2)
        {
            scanf("%d", &a);
            f[i] = f[lastans^a];
        }
        else if (op == 3)
        {
            scanf("%d%d", &a, &b);
            printf("%d\n", lastans = find(i, a ^ lastans) == find(i, b ^ lastans));
        }
    }
    return 0;
}

可持久化並查集加強版 BZOJ 3674