淺談並查集

tyccyt發表於2024-11-28

普通並查集

就是開一個 \(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

相關文章