zoukankan      html  css  js  c++  java
  • 归纳(一):并查集

    我眼中的并查集

    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
    ",ans[i]);
        return 0;
    }
    

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

    可持久化并查集。


    博客里有写。

    再贴一遍代码

    #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
    ",siz1);
    			}
    		}
    	}
    }
    
    int main(){
    	freopen(FN".in","r",stdin);
    	freopen(FN".out","w",stdout);
    	HJT::work();
    	return 0;
    }
    

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

  • 相关阅读:
    HDU 3911 线段树区间合并
    Memcache启动&amp;存储原理&amp;集群
    剑指Offer面试题27(Java版):二叉搜索树与双向链表
    ORA-12514: TNS: 监听程序当前无法识别连接描写叙述符中请求的服务
    Java.Lang.NoSuchMethod 错误
    poj2524
    特征价格(Hedonic price)
    特征价格(Hedonic price)
    苏州之行
    苏州之行
  • 原文地址:https://www.cnblogs.com/LoLiK/p/9739820.html
Copyright © 2011-2022 走看看