帶撤銷並查集支援從某個元素從原來的集合中撤出來,然後加入到乙個另外乙個集合中,或者刪除該元素
用乙個對映來表示元素和並查集中序號的關係,**中用\(to[x]\) 表示x號元素在並查集中的 id
刪除 x 號元素時,需要將 \(to[x]\) 的集合大小減去1,然後令 \(to[x]=-1\) 標記 x 刪除即可
如果要重新加入乙個元素,那麼給x分配乙個新的 id,\(to[x] = newid\)
例題1:
#include using namespace std;
const int n = 4000010;
int fa[n],to[n],sz[n],cnt,a[n],b[n],tot;
int n,m,k,x,y;
int ok[n];
int find(int x)
void merge(int x,int y)
// 將x從原集合刪除,加入到 y 所屬的集合
void update(int x,int y)
int main()
if(k == 1)merge(x,y);
if(k == 2)update(x,y);
if(k == 4)
if(k == 3)printf("%d\n",sz[find(to[x])]-1);
}for(int i=1;i<=tot;i++)
int res = -1;
for(int i=1;i<=n;i++)
if(!ok[find(to[i])])res = max(res,sz[find(to[i])]);
cout《例題2:
#includeusing namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do while (0)
void err()
templatevoid err(const t& arg,const ts&... args)
const int n = 1000000 + 50;
struct nodeo[n];
vectorg[n];
int n, m;
int fa[n*2], dep[n*2], sz[n*2], to[n], tot;
int res[n];
int find(int x)
/*1 k a b merge(a, b)
2 k a del(a)
3 k a b merge(a,b)
4 k a b find(a) == find(b)
5 k a sz[a]
*/void dfs(int u)
int rx = find(to[x]), ry = find(to[y]);
if(rx == ry)
else if(dep[rx] == dep[ry]) else
} else if(op == 2)
int rx = find(to[x]);
int t = to[x];
to[x] = -1;
sz[rx] --;
dfs(id);
to[x] = t;
sz[rx] ++;
} else if(op == 3)
int t = to[x];
int rx = find(to[x]), ry = find(to[y]);
sz[rx]--;
to[x] = ++tot;
sz[to[x]] = 1;
fa[to[x]] = ry;
sz[ry] ++;
dfs(id);
sz[ry] --;
sz[rx] ++;
to[x] = t;
} else else
} else if(op == 5)
}dfs(id);}}
}int main();
}for(int i=1;i<=n;i++) to[i] = i, fa[i] = i, sz[i] = 1;
tot = n;
dfs(0);
for(int i=1;i<=m;i++)
}return 0;
}
可持久化並查集支援查詢任一歷史版本的資訊。並查集資訊用陣列 \(fa\) 表示,合併集合時,基本操作有兩種,乙個是路徑壓縮(可能會修改很多個fa),乙個是按秩合併(啟發式合併思路,小的集合向大的合併,只會修改一次fa),由於要儲存歷史資訊,按照可持久化的一貫思路,每一次操作都會新開 logn 的空間,所以這裡要用按秩合併。
注意按秩合併的find函式寫法也有所不同,不能修改fa
此時問題就是維護每個版本的fa陣列
模版題:
const int n = 200000 + 5;
int n, m;
struct treenodet[n*30];
int root[n], tot;
int fa[n*30], dep[n*30];
void build(int &p, int l, int r)
int mid = l + r >> 1;
build(t[p].l, l, mid);
build(t[p].r, mid+1, r);
}// 修改p版本的pos位置的fa值
void change(int last, int &p, int l, int r, int pos, int val)
int mid = l + r >> 1;
if(pos <= mid)
change(t[last].l, t[p].l, l, mid, pos, val);
else
change(t[last].r, t[p].r, mid+1, r, pos, val);
} int getindex(int p, int l, int r,int pos)
int find(int p, int x)
void merge(int last, int &p, int x, int y)
}int main()
else if(op == 2) root[i] = root[x];
else
}return 0;
}
例題:
先跑一次最短路,然後按照邊權從大到小用帶權並查集維護集合到源點的最短距離。保留所有版本的資訊,然後對於每次查詢找到對應版本號回答問題即可
複雜度:\(o(n\log m + (m+q) \log^2n)\)
可持久化並查集 可撤銷並查集
主要學習的blog 1 將乙個點的父親 並查集那個fa 進行更改 實際上是新增乙個資訊點 update。2 查詢某個時間點下乙個pos對應的資訊點編號。3 查詢乙個點的父節點。4 更新乙個點的deep值,不用新寫函式,可以用3號操作找到編號後 5 初始化build。pragma gcc optimi...
演算法筆記 可撤銷並查集 可持久化並查集
可撤銷並查集模板 struct ufs inline int find int x inline void merge int x,int y fa x y if rnk x rnk y rnk y else fa y x inline void undo ufs 可持久化並查集模板 struct ...
可持久化並查集
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是上一次的輸出答案 並查集實質是乙個陣列,可持久化並查...