并查集的几道好题

并查集的应用

将一些东西合并在一个集合。
通常的应用就有
1、并的优化
2、查的优化

普通的并查集

直接连边,查询的时候暴力往上跳。

并的优化

由于本人弱,目前只知道按秩合并。
所谓秩,就是一个值,在进行合并的时候,比较2个集合的秩,秩小的向秩大的合并。
这样并查集的高度就是 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 )
写点伪代码:

void get(int x){
    return fa[x]==x?x:get(f[x]);
}

好的题目!

#1 [TJOI2016]树

有一棵树,两个操作。1标记一个点。2询问最近一个打了标记的祖先。
一开始只有根节点有标记。

最近打了标记的祖先。
考虑一条链怎么做。实际上给x~y之间的点z打标记,相当于将点分为2个集合。
所以倒过来做,就变成合并集合的操作了。

#2 JZOJ 5670

这题 按秩合并。
题解地址

#3 JZOJ 5798

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的方向的情况。
有时候拿个数组来表示约束条件

猜你喜欢

转载自blog.csdn.net/huangjingyuan107/article/details/81590161