【学习笔记】基础算法——并查集

并查集

二话不说上模板!

模板题。

这不大水吗,都毫无难度。只要写好一个 FF 函数一切就完事儿了。

int FF(int u){return (u==fa[u]?u:fa[u]=FF(fa[u]));}

冷知识:大家一般都取做 Findfind 之类的,我这函数叫 FF 这名儿是什么意思呢?其实原本我也是叫 Find 的,但是我觉得这名讲得不够清楚,就又用了一段时间的 Find_Father,可是太长了,多难敲啊,因此我就取了 FindFather 的首字母,所以这函数就变成 FF 了……笑啥,又好敲,又好记,还有详细含义,还少见,多棒啊!(

代码就不贴了,相信不到 \(20\) 行就能轻松搞定吧,我有啥贴代码的必要呢。

拓展域并查集

好文推广。

说白了可以理解为那啥的“敌人的敌人是朋友”。

哎这样讲多不明白啊,搬仨题出来吧。

例题——关押罪犯

很不错的题。

首先肯定贪心嘛,冲突大的万不可放一块儿。

那么每个罪犯就抽象为一个点 \(u\)\(u\) 的反状态是 \(u+n\)

假若我们不想让 \(u\)\(v\) 关一块儿,那就让它们各自与对方的反状态关一块儿去,也就是说连俩边,一个是 \((u,v+n)\),另一个是 \((u+n,v)\)

什么时候不得不关一块儿呢?当且仅当不想让 \(u\)\(v\) 关一块儿,已经连完反状态边以后,存在某个点 \(x\) 满足 \(x\)\(x+n\) 处于一个集合里了。那就不行了!那么这一次的 \(u\)\(v\) 就不得不关一块儿了。这个时候输出答案并 return 0 就是啦!

那么本题就结束了。

还是贴一下代码吧,你说呢?

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+5 , M = 1e5+5;
struct line{int u,v,w;}ln[M];int n,m,fa[2*N];
bool cmp(line l1,line l2){return l1.w>l2.w;}
int FF(int u){return (fa[u]==u?u:fa[u]=FF(fa[u]));}
void Merge(int u,int v){if(FF(u)!=FF(v))fa[FF(v)]=FF(u);return;}
int main(){
    cin>>n>>m;
    for(int i=1;i<=2*n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)cin>>ln[i].u>>ln[i].v>>ln[i].w;
    sort(ln+1,ln+m+1,cmp);
    for(int i=1;i<=m;i++){
        Merge(ln[i].u+n,ln[i].v),Merge(ln[i].u,ln[i].v+n);
        if(FF(ln[i].u)==FF(ln[i].u+n)||FF(ln[i].v)==FF(ln[i].v+n)){cout<<ln[i].w<<"\n";return 0;}
    }
    cout<<"0\n";return 0;
}

习题——The Door Problem

由于用洛谷可以比较方便的看到翻译所以给的是洛谷的链接。当然啦,CF 链接

这跟上面那个题很像吧!

只是说对于开着的,就合并 \((u,v)\) 以及 \((u+m,v+m)\),而对于关着的,才合并 \((u,v+m)\)\((u+m,v)\)

同样的只要存在某个 \(x\) 满足 \(x\)\(x+m\) 待一个集合里了那么就无解啦。否则有解!

贴代码!

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int n,m,p[2][N],cnt[N],fa[N];bool a[N],flag;
int FF(int u){return (fa[u]==u?u:fa[u]=FF(fa[u]));}
void Merge(int u,int v){fa[FF(v)]=FF(u);return;}
int main(){
    cin>>n>>m;flag=1;
    for(int i=1;i<=n;i++)cin>>a[i];
    memset(cnt,-1,sizeof(cnt));
    for(int i=1;i<=m;i++){
        int ct;cin>>ct;
        while(ct--){int x;cin>>x;p[++cnt[x]][x]=i;}
    }
    for(int i=1;i<=2*m;i++)fa[i]=i;
    for(int i=1;i<=n;i++)
        if(!a[i])Merge(p[0][i],p[1][i]+m),Merge(p[0][i]+m,p[1][i]);
        else Merge(p[0][i],p[1][i]),Merge(p[0][i]+m,p[1][i]+m);
    for(int i=1;i<=m;i++)if(FF(i)==FF(i+m))flag=0;
    if(flag)cout<<"YES\n";else cout<<"NO\n";
    return 0;
}

作业

食物链!

这个是三倍的,也就是说存在 \(u\)\(u+n\) 以及 \(u+2n\)。因为有三种动物循环吃,所以这是三倍的扩展域并查集。

总结

并查集!

并查集本身不难但套上乱七八糟的一大堆东西以后就不简单啦。各种各样的方法,各种各样的情况下,包括各种变种。很难的呐!

各种运用,各种创新。不断挖掘!

posted @ 2025-07-19 10:41  嘎嘎喵  阅读(43)  评论(2)    收藏  举报