浅谈并查集

普通并查集

就是开一个 \(fa[i]\) 数组表示 \(i\) 的祖先节点。

初始化

for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
//fa[i]初始状态一定是只像自己的,siz[i]:表示 以 i 为根的子树大小

查询

inline int getf(int x)
{
    if(x==fa[x])return x;
    return getf(fa[x]);
}

合并

inline void merge(int u,int v)
{
    u=getf(u),v=getf(v);
    if(u!=v)
    {
        fa[u]=v;
    }
}

优化

路径压缩

这个优化在查询中,就是在返回自己最高级祖先时,顺便更新自己的值。

inline int getf(int x)
{
    if(x==fa[x])return x;
    return fa[x]=getf(fa[x]);//就是这里
}

按秩合并

虽然在merge(u,v)中,但是优化的是查询,原理是子树小的接子树大的(这也是树上启发式合并的雏形)。

inline void merge(int u,int v)
{
    u=getf(u),v=getf(v);
    if(u!=v)
    {
        if(siz[u]<siz[v])swap(u,v);
        f[v]=u,siz[u]+=siz[v];//v小u大
    }
}

扩展域并查集

根据名字,扩展域并查集的含义就是将领域扩展。

经典题目

比如说扩展至 \(2*n\),那么可以用 \([1,n]\) 表示本身,\([n+1,2*n]\) 表示敌人(比如说 \(i\) 的敌人就是 \(i+n\)),非常好理解。

可撤销并查集

根据名字,可撤销并查集的含义就是能够撤销的并查集(但是只能按照加入的时间先后顺序撤销)。

直接上代码讲解:

int n,fa[N],siz[N],st[N],top=0;
il void getf(int x)
{
    if(x==fa[x])return x;
    return getf(fa[x]);
    //由于后面需要支持撤销,故这里不因使用路径压缩,fa[i]数组只记录i的一级祖先
}
il void merge(int u,int v)
{
    u=getf(u),v=getf(v);
    if(u!=v)
    {
        if(siz[u]<siz[v])swap(u,v);
        fa[v]=u,siz[u]+=siz[v];
    }
}
il void upd()///删除上一条加入的边
{
    if(top==0)return;
    int x=s[top--];
    siz[fa[x]]-=siz[x],fa[x]=x;
    //将 x 从他父亲底下扯下去(删除 x 到 fa[x] 这条边更新siz,fa数组)
}

可持久化并查集

因该是个好东西,可惜我只会可持久化线段树Q_Q

posted @ 2024-11-28 19:25  tyccyt  阅读(5)  评论(0编辑  收藏  举报