归纳(一):并查集

我眼中的并查集

Round 1:朴素并查集

int fa[maxn];

int find(int x) {
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void un(int x,int y) {
    fa[find(x)]=find(y);
}

Round 2:按秩合并

int fa[maxn],siz[maxn];

int find(int x) {
    return fa[x]==x?x:find(fa[x]);
}

void un(int x,int y) {
    if(siz[x=find(x)]>siz[y=find(y)])
        fa[x]=y,siz[y]+=siz[x];
    else fa[y]=x,siz[x]+=siz[y];
}

例题1:修复公路

题面

可以算联通块,也可以按秩合并+路径压缩

联通了,就并到一起,siz加起来,如果siz=n,就直接输出时间即可。

10min切。

代码:

#include<bits/stdc++.h>

const int maxn=1000+5;
const int maxm=1e5+5;

int fa[maxn],siz[maxn];

struct Edge {
    int x,y,t;
}e[maxm];

int find(int x) {
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

void un(int x,int y) {
    if(x==y) return ;
    fa[x]=y;
    siz[y]+=siz[x];
}

bool cmp(const Edge &a,const Edge &b) {
    return a.t<b.t;
}

int main() {
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].t);
    std::sort(e+1,e+m+1,cmp);
    for(int i=1;i<=m;i++) {
        int xx=find(e[i].x),yy=find(e[i].y);
        un(xx,yy);
        if(siz[yy]==n) return !printf("%d",e[i].t);
    }
    printf("-1");
    return 0;
}

很长时间里,我以为这就是并查集的全部内容了。

直到:

Round 3:种类并查集

也许叫这个名字,我也不清楚。

并查集可以很好表示亲戚的亲戚是亲戚这种关系,但是对于敌人的敌人是朋友这种情况就显得无能为力。

使用两倍的并查集,i表示相同,i+n表示相对,于是就有了相对的相对是相同的关系。

例题(二):食物链

题面

我的猎物的猎物是我的天敌。(真的违反自然规律好吧)

于是开三倍大小的并查集,i表示同类,i+n表示猎物,i+n+n表示天敌。

于是就解决了。

#include<bits/stdc++.h>

const int maxn=5e4+5;

int fa[maxn*3];

int find(int x) {
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

int main() {
    int n,k;scanf("%d%d",&n,&k);
    for(int i=1;i<=n*3;i++) fa[i]=i;
    int ans=0;
    while(k--) {
        int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
        if(x>n || y>n) {++ans;continue;}
        if(opt==1) {
            if(find(x)==find(y+n) || find(y)==find(x+n)) ++ans;
            else for(int i=0;i<3;i++) fa[find(x+i*n)]=find(y+i*n);
        }
        else {
            if(find(x)==find(y) || find(x)==find(y+n)) ++ans;
            else for(int i=0;i<3;i++) fa[find(x+(i+1)%3*n)]=find(y+i*n);
        }
    }
    printf("%d",ans);
    return 0;
}

例题(三):星球大战

题面

要建图,先全部毁于一旦,在从后往前时光倒流,做法跟修复公路计算连通块又异曲同工之妙。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

const int maxn=1e6+5;

struct Edge {
    int nxt,v,frm;
}e[maxn];

int h[maxn],tot;

void add_edge(int u,int v) {
    e[++tot].v=v;
    e[tot].nxt=h[u];
    h[u]=tot;
    e[tot].frm=u;
}

int fa[maxn];

int find(int x) {
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

int z[maxn];
bool vis[maxn];
int ans[maxn];

int main() {
    int n,m;scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) fa[i]=i,h[i]=-1;
    for(int i=0;i<m;i++) {
        int x,y;scanf("%d%d",&x,&y);
        add_edge(x,y);
        add_edge(y,x);
    }
    int k;scanf("%d",&k);
    int cnt=n-k;
    for(int i=1;i<=k;i++) {
        scanf("%d",z+i);
        vis[z[i]]=true;
    }
    for(int i=0;i<m+m;i++) {
        if((!vis[e[i].frm]) && (!vis[e[i].v]) && (find(e[i].frm)!=find(e[i].v)))
            fa[find(e[i].frm)]=find(e[i].v),--cnt;
    }
    ans[k+1]=cnt;
    for(int t=k;t>0;t--) {
        vis[z[t]]=false;
        ++cnt;
        for(int i=h[z[t]];~i;i=e[i].nxt) {
            if((!vis[e[i].v]) && (find(e[i].v)!=find(z[t])))
                fa[find(e[i].v)]=find(z[t]),--cnt;
        }
        ans[t]=cnt;
    }
    for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]);
    return 0;
}

掌握了这几道,感觉基本可以说是较全面的掌握了并查集。
(其实只是过了试练场罢了)

可持久化并查集。

1447768-20181003151237646-1768649085.png
1447768-20181003151248882-2143128346.png

博客里有写。

再贴一遍代码

#include<bits/stdc++.h>
#define FN "build"

const int maxn=1e5+5;

inline int read() {
    int x;char ch;bool flag=false;while(!isdigit(ch=getchar()))
    (ch=='-') && (flag=true);
    for(x=ch-'0';isdigit(ch=getchar());x=(x<<3)+(x<<1)+ch-'0');
    return (flag?-x:x);
}

namespace HJT {
    
    int n;
    
    struct Node {
        Node *ls,*rs;
        int siz,fa;
    }pool[maxn*32], *root[maxn], *zero, *tail=pool;
    
    Node *newnode() {
        Node *nd=++tail;
        nd->ls=nd->rs=zero;
        nd->siz=nd->fa=0;
        return nd;
    }
    
    Node *Fmodify(Node *p,int l,int r,int pos,int rt) {
        Node *nd=newnode();
        if(l==r) {
            nd->fa=rt;
            nd->siz=p->siz;
            return nd;
        }
        int mid=l+r>>1;
        if(pos<=mid) {
            nd->rs=p->rs;
            nd->ls=Fmodify(p->ls,l,mid,pos,rt);
        }
        else {
            nd->ls=p->ls;
            nd->rs=Fmodify(p->rs,mid+1,r,pos,rt);
        }
        return nd;
    }
    
    Node *Smodify(Node *p,int l,int r,int pos,int rt) {
        Node *nd=newnode();
        if(l==r) {
            nd->fa=p->fa;
            nd->siz=p->siz+rt;
            return nd;
        }
        int mid=l+r>>1;
        if(pos<=mid) {
            nd->rs=p->rs;
            nd->ls=Smodify(p->ls,l,mid,pos,rt);
        }
        else {
            nd->ls=p->ls;
            nd->rs=Smodify(p->rs,mid+1,r,pos,rt);
        }
        return nd;
    }
    
    int Fquery(Node *nd,int l,int r,int pos) {
        if(l==r) return nd->fa;
        int mid=l+r>>1;
        if(pos<=mid) return Fquery(nd->ls,l,mid,pos);
        else return Fquery(nd->rs,mid+1,r,pos);
    }
    
    int Squery(Node *nd,int l,int r,int pos) {
        if(l==r) return nd->siz;
        int mid=l+r>>1;
        if(pos<=mid) return Squery(nd->ls,l,mid,pos);
        else return Squery(nd->rs,mid+1,r,pos);
    }
    
    int find(int i,Node *nd,int x) {
        int ff=Fquery(nd,1,n,x);
        if(ff==x) return x;
        return find(i,nd,ff);
    }
    
    Node *build(int l,int r) {
        Node *nd=newnode();
        if(l==r) {
            nd->siz=1;
            nd->fa=l;
            return nd;
        }
        int mid=l+r>>1;
        nd->ls=build(l,mid);
        nd->rs=build(mid+1,r);
        return nd;
    }
        
    void work() {
        int m=read();n=read();
        int lastans=0,tot=0;
        zero=++tail;
        zero->ls=zero->rs=zero;
        zero->fa=zero->siz=0;
        root[0]=build(1,n);
        for(int i=1;i<=m;i++) {
            int opt=read();
            int u=lastans+read();
            int v=lastans+read();
            if(opt==1) {
                u=find(tot,root[tot],u);
                v=find(tot,root[tot],v);
                if(u==v) {
                    ++tot;
                    root[tot]=root[tot-1];
                    continue;
                }
                int siz1=Squery(root[tot],1,n,v);
                int siz2=Squery(root[tot],1,n,u);
                if(siz1>siz2) {
                    std::swap(siz1,siz2);
                    std::swap(u,v);
                }
                ++tot;
                root[tot]=Fmodify(root[tot-1],1,n,v,u);
                root[tot]=Smodify(root[tot],1,n,u,siz1);
            }
            else {
                int ff=find(u,root[u],v);
                int siz1=Squery(root[u],1,n,ff);
                root[tot+1]=root[tot];
                ++tot;
                lastans=siz1;
                printf("%d\n",siz1);
            }
        }
    }
}

int main(){
    freopen(FN".in","r",stdin);
    freopen(FN".out","w",stdout);
    HJT::work();
    return 0;
}

可持久化并查集真的不知道还算不算并查集,真的区别太大了。

转载于:https://www.cnblogs.com/LoLiK/p/9739820.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值