将一些东西合并在一个集合。
通常的应用就有
1、并的优化
2、查的优化
直接连边,查询的时候暴力往上跳。
由于本人弱,目前只知道按秩合并。
所谓秩,就是一个值,在进行合并的时候,比较2个集合的秩,秩小的向秩大的合并。
这样并查集的高度就是 O(log) O ( l o g ) 的。
可以维护一些信息。比如说size,并查集中点与点、边与边之间的信息。
还可以撤销合并的操作。(用栈维护一下就好了)
写点伪代码:
void merge(int x,int y){
if(rt[x]>rt[y])swap(x,y);
if(rt[x]==rt[y])rt[y]++;
fa[x]=y;
stack...
}
void ctrl_z(int x,int y){
stack... rank[y]=...
fa[x]=0;
}
这是并查集的一种启发式合并。
由于本人弱,目前只知道路径压缩。
有的时候我们只想知道两个元素是否在一个集合(或在哪个集合)
时间复杂度: O(n α n) O ( n α n )
写点伪代码:
void get(int x){
return fa[x]==x?x:get(f[x]);
}
有一棵树,两个操作。1标记一个点。2询问最近一个打了标记的祖先。
一开始只有根节点有标记。
最近打了标记的祖先。
考虑一条链怎么做。实际上给x~y之间的点z打标记,相当于将点分为2个集合。
所以倒过来做,就变成合并集合的操作了。
这题 按秩合并。
题解地址
n个点的树,您将从树中获得M个节点对,形式为(a1,b1),(a2,b2),…(am,bm).
给每一条边定向,使得每一对节点对存在一条从ai到bi或从bi到ai的路径。
现在要求方案数,对10^9+7取mod即可。
维护点的信息
任意两个点,在树上只有唯一一条路径。
根据这个可以想到并查集。
将路径(ai,bi)上的边合并。
从深度大的向深度小的连边。
合并的时候同时维护一下边的信息:定义“方向”表示x与fa[x]的连边方向。
显然,一个集合内的所有边的”方向”是相同的。
如果并查集树上x集合的边和y集合的边的“方向”相同,那么无解。
考虑并查集树上有什么信息可以维护。
设f[x]表示边x的并查集树根到边x的方向的情况。
有时候拿个数组来表示约束条件。