一、扩展域并查集
适用情况:涉及到2种或3种相互关系的冲突判断时使用。
例如划分两个集合,有互斥关系的人不在一个集合,有友好关系的人得在同一个集合,判断能不能划分成满足要求不冲突的两个集合。
具体操作:将并查集扩大一倍,x表示本身,x+n表示冲突的反节点。表示友好关系时,x和y以及x+n和y+n合并;表示互斥关系时,x+n和y以及y+n和x合并。
进行冲突判断时,如果表述为友好,但x+n和y(或x和y+n)已经在同一集合,则冲突;
如果表述为互斥,但x+y(或x+n和y+n)已经在同一集合,则冲突。
二、加权并查集
对边添加权值,对节点设置数组d[i]表示i节点到树根的权值总和(即到树根距离)。在路径压缩时向上搜索到树根后向下更新d[i];合并时调整将要成为儿子的元祖节点的d值,使更改后两子树中节点的相对深度不变。
更新操作:
int find(int x)
{
if(fa[x]==x) return x;
int fx=fa[x];//保存父节点,只加父节点的d值
fa[x]=find(fa[x]);
d[x]+=d[fx];//更新d值
// d[x]%=mod 有时需要取模
return fa[x];
}
合并操作:
void join(int x,int y,int w)//w为x,y相对距离
{
int fx=find(x),fy=find(y);
if(fx==fy) return;
fa[fy]=fx;
d[fy]=(d[x]-d[y]-w+Mod)%Mod;
}
更改被合并的儿子节点d[fy]使x,y的相对距离w不变
因为以fx为根,则x到根距离不变,y到根距离为d[fy]+d[y]
则 ,推导得
三、启发式合并
可以增加一个树高h数组,将深度小的树合并到深度大的树下,合并后大树深度不变;若两树深度相同,合并后深度+1
int join(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx==fy) return;
if(h[fx]>h[fy]) fa[fy]=fx;
else fa[fx]=fy;
if(h[fx]==h[fy]) h[fy]++;//因为相等的情况是合并到fy的
}