• 并查集


    并查集

    关键操作

    1.预处理

    for(int i=1;i<=m;i++) fa[i]=i;
    

    2.路径压缩O(n)

    int find(int x) {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    3.merge
    int fx=find(a[i].x),fy=find(a[i].y);    
    fa[fx]=fy;
    

    3.按秩合并O(nlogn)

    秩的意思就是树的高度,按秩合并过后并查集的结构为树形结构

    程序自动分析

    第一步当然是先离散化

    然后对于“=”操作,我们直接merge

    对于“≠”操作,我们check一下他们如果在一个联通块中就 return no,否则不动

    然后小trick,我们可以按 e 排个序,让 “=” 操作先执行

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    
    int T,n;
    int fa[1000005];
    struct node{
        long long x,y;int z;
        bool operator < (const node &x) const {
            return z>x.z;
        }
    }a[1000005];
    long long b[2000050];
    int m;
    int find(int x) {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    bool flag;
    int main() {
        T=read();
        while(T--) {
            n=read();m=0;flag=0;
            for(int i=1,x,y,z;i<=n;i++) {
                scanf("%lld%lld",&a[i].x,&a[i].y);a[i].z=read();
                b[++m]=a[i].x;b[++m]=a[i].y;
            }
            sort(b+1,b+1+m);
            m=unique(b+1,b+1+m)-b-1;
            for(int i=1;i<=n;i++) {
                a[i].x=lower_bound(b+1,b+1+m,a[i].x)-b;
                a[i].y=lower_bound(b+1,b+1+m,a[i].y)-b;
            }
            sort(a+1,a+1+n);
            for(int i=1;i<=m;i++) fa[i]=i;
            for(int i=1;i<=n;i++) {
                int fx=find(a[i].x),fy=find(a[i].y);
                if(a[i].z==1) {
                    fa[fx]=fy;
                } else {
                    if(fa[fx]==fa[fy]) {
                        flag=1;break;
                    }
                }
            }
            if(flag) puts("NO");
            else puts("YES");
        }
        return 0;
    }
    

    supermarket

    solution

    简而言之,就是维护一个时间并查集,一开始的时间都是截止时间卖出,然后按利润从大到小排序(贪心),卖了这个就把其余的这天卖出的和前一天卖出的合并,然后每次 x=find(a[i].d)找到的都是能卖出的最靠后的天数

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    int n,mxtime;
    int fa[10005];
    struct node{
        int p,d;
        bool operator < (const node &x) const {
            return p>x.p;
        }
    }a[10005];
    int find(int x) {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    int ans;
    int main() {
        while(scanf("%d",&n)!=EOF) {
            ans=0;mxtime=0;
            for(int i=1;i<=n;i++) {
                scanf("%d%d",&a[i].p,&a[i].d);
                mxtime=max(mxtime,a[i].d);
            }
            for(int i=1;i<=mxtime;i++)
                fa[i]=i;
            sort(a+1,a+1+n);
            for(int i=1;i<=n;i++) {
                int x=find(a[i].d);
                if(x>0) {
                    ans+=a[i].p;
                    fa[x]=x-1;
                }
            }
            printf("%d
    ",ans); 
        }
        return 0;
    }
    

    [NOI2002]银河英雄传说

    边带权并查集

    维护siz和 d[]表示当前这个点到 fa[x] 的距离

    然后每次合并注意先不要直接直接路径压缩fa

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n;
    int fa[30005],d[30005],siz[30005];
    int find(int x) {
        if(x==fa[x]) return x;
        int ans=find(fa[x]);
        d[x]+=d[fa[x]];
        return fa[x]=ans;
    }
    void merge(int x,int y) {
        fa[x]=y;
        d[x]+=siz[y];
        siz[y]+=siz[x];
        siz[x]=0;
    }
    int abs(int op) {
    	return op<0?-op:op;
    }
    int x,y;
    char c;
    int main() {
        n=read();
        for(int i=1;i<=30000;i++) fa[i]=i,siz[i]=1;
        for(int i=1;i<=n;i++) {
            cin>>c;
            x=read();y=read();
            int fx=find(x),fy=find(y);
            if(c=='M') merge(fx,fy);
            else {
                if(fx!=fy) puts("-1");
                else printf("%d
    ",abs(d[y]-d[x])-1);
            }
        }
        return 0;
    }	
    

    Parity game

    解答见蓝书p198

    边带权并查集

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int N=40010;
    int n,m;
    struct node{
        int l,r;int val;
    }q[N];
    int b[N],len;
    int fa[N],d[N];
    char c[5];
    int find(int x) {
        if(x==fa[x]) return x;
        int ans=find(fa[x]);
        d[x]^=d[fa[x]];
        return fa[x]=ans;
    }
    int main() {
        n=read();m=read();
        for(int i=1;i<=m;i++) {
            q[i].l=read();q[i].r=read();scanf("%s",c);
            q[i].val=(c[0]=='o'?1:0);
            b[++len]=q[i].l-1; b[++len]=q[i].r;
        }
        sort(b+1,b+1+len);
        n=unique(b+1,b+1+len)-b-1;
    
        for(int i=1;i<=2*n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) {
            int x=lower_bound(b+1,b+1+n,q[i].l-1)-b-1;
            int y=lower_bound(b+1,b+1+n,q[i].r)-b-1;
            int fx=find(x),fy=find(y);
            if(fx==fy) {
                 if((d[x]^d[y])!=q[i].val) {
                     printf("%d
    ",i-1);
                     return 0;
                 }
            } else {
                fa[fx]=fy;
                d[fx]=d[x]^d[y]^q[i].val;
            }
        }
        printf("%d
    ",m);
        return 0;
    }
    

    扩展域并查集

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int N=40010;
    int n,m;
    struct node{
        int l,r;int val;
    }q[N];
    int b[N],len;
    int fa[N],d[N];
    char c[5];
    int find(int x) {
        if(x==fa[x]) return x;
        int ans=find(fa[x]);
        d[x]^=d[fa[x]];
        return fa[x]=ans;
    }
    int main() {
        n=read();m=read();
        for(int i=1;i<=m;i++) {
            q[i].l=read();q[i].r=read();scanf("%s",c);
            q[i].val=(c[0]=='o'?1:0);
            b[++len]=q[i].l-1; b[++len]=q[i].r;
        }
        sort(b+1,b+1+len);
        n=unique(b+1,b+1+len)-b-1;
    
        for(int i=1;i<=2*n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) {
            int x=lower_bound(b+1,b+1+n,q[i].l-1)-b;
            int y=lower_bound(b+1,b+1+n,q[i].r)-b;
            int x_odd=x,x_even=x+n;
            int y_odd=y,y_even=y+n;
            if(q[i].val==0) {
                if(find(x_odd)==find(y_even)) {
                    printf("%d
    ",i-1);
                    return 0;
                }
                fa[find(x_odd)]=find(y_odd);
                fa[find(x_even)]=find(y_even);
            } else {
                if(find(x_odd)==find(y_odd)) {
                    printf("%d
    ",i-1);
                    return 0;
                }
                fa[find(x_odd)]=find(y_even);
                fa[find(x_even)]=find(y_odd);
            }
        }
        printf("%d
    ",m);
        return 0;
    }
    

    食物链

    扩展域并查集

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    const int N=150010;
    int n,m,ans;
    int fa[N];
    char c[5];
    int find(int x) {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    int main() {
        n=read();m=read();
    
        for(int i=1;i<=3*n;i++) fa[i]=i;
    
        for(int i=1;i<=m;i++) {
            int val=read(),x=read(),y=read();
            if(x>n||y>n) {
                ans++;
                continue;
            }
            if(x==y&&val==2) {
                ans++;
                continue;
            }
            int x_self=x,x_enemy=x+n,x_eat=x+2*n;
            int y_self=y,y_enemy=y+n,y_eat=y+2*n;
            if(val==1) {
                if(find(x_eat)==find(y_self) || find(y_eat)==find(x_self)) {
                    ans++;
                    continue;
                }
                fa[find(x_eat)]=find(y_eat);
                fa[find(x_enemy)]=find(y_enemy);
                fa[find(x_self)]=find(y_self);
            } else {
                if(find(y_eat)==find(x_self) || find(x_self)==find(y_self)) {
                    ans++;
                    continue;
                }
                fa[find(x_eat)]=find(y_self);
                fa[find(x_self)]=find(y_enemy);
                fa[find(x_enemy)]=find(y_eat);            
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    关押罪犯

    排序,我们要尽可能让危害大的罪犯在两个监狱里。

    那么,再结合敌人的敌人和自己在一个监狱的规律合并。

    当查找时发现其中两个罪犯不可避免地碰撞到一起时,只能将其输出并结束。

    还有一点很重要,就是没有冲突时一定输出0!!!

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m;
    int fa[100005],enemy[100005];
    struct node{
        int x,y,z;
        bool operator < (const node &x) const {
            return z>x.z;
        }
    }a[100005];
    int find(int x) {
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    void merge(int x,int y) {
        x=find(x),y=find(y);
        fa[x]=y;
    }
    int main() {
        n=read();m=read();
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) {
            a[i].x=read();a[i].y=read();a[i].z=read();
        }
        sort(a+1,a+1+m);
        for(int i=1;i<=m;i++) {
            if(find(a[i].x)==find(a[i].y)) {printf("%d
    ",a[i].z);return 0;}
            if(!enemy[a[i].x]) enemy[a[i].x]=a[i].y;
            else merge(enemy[a[i].x],a[i].y);
            if(!enemy[a[i].y]) enemy[a[i].y]=a[i].x;
            else merge(enemy[a[i].y],a[i].x);
        }
        puts("0");
        return 0;
    }
    

    CF85E Guard Towers

    排个序,从大到小

    和上题类似,连自己和对方的敌人,直到两人或敌人的find相同,这是ans=i ;

    然后再求连通块数目得到方案数

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    #define pii pair<int,int> 
    #define MP make_pair
    using namespace std;
    const int N=100005;
    const int P=1e9+7;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    inline void Max(int &x,int y){if(x<y)x=y;}
    inline void Min(int &x,int y){if(x>y)x=y;}
    
    int n,x[N],y[N];
    long long ans;
    vector< pii >v[N];
    int fa[N];
    inline int find(int x) {
    	return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    inline int jue(int x) {return x>0?x:(-x);}
    inline int dist(int i,int j) {
    	return jue(x[i]-x[j])+jue(y[i]-y[j]);
    }
    int main() {
    	n=read();
    	for(int i=1;i<=n;i++) {
    		x[i]=read();y[i]=read();
    		for(int j=1;j<i;j++) 
    			v[dist(i,j)].push_back(MP(i,j));
    	}
    	for(int i=1;i<=2*n;i++) fa[i]=i;
    	for(int i=10000;i>=0;i--) {
    		for(auto t: v[i]) {
    			int x=find(t.first),y=find(t.second);
    			int ex=find(t.first+n),ey=find(t.second+n);
    			if(x==y||ex==ey) {
    				ans=i;break;
    			}
    			fa[x]=ey;fa[y]=ex;
    		}
    		if(ans) break;
    	}
    	printf("%lld
    ",ans);
    	for(int i=1;i<=n;i++) fa[i]=i;
    	for(int i=ans+1;i<=10000;i++) {
    		for(auto t: v[i]) {
    			int x=find(t.first),y=find(t.second);
    			fa[x]=y;
    		}
    	}
    	ans=1;
    	for(int i=1;i<=n;i++) 
    		if(fa[i]==i)
    			ans=ans*2%P;
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    
    

    UVA 11354 - Bond

    #include <cstdio>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=2e5+5;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    struct Edge{
    	int u,v,w;
    	bool operator < (const Edge &x) const {
    		return w<x.w;
    	}
    }e[N];
    int hd[N],tot;
    int n,m,fa[N],siz[N],val[N];
    int find(int x) {
    	return x==fa[x]?x:find(fa[x]);
    }
    void merge(int x,int y,int z) {
    	x=find(x),y=find(y);
    	if(x==y) return;
    	if(siz[x]<siz[y]) swap(x,y);
    	fa[y]=x;val[y]=z;
    	if(siz[x]==siz[y]) siz[x]++;
    }
    int v[N];
    int query(int x,int y) {
    	for(int i=0;i<=n;i++) v[i]=0;
    	int ans=1,ans1=0;
    	while(1) {
    		v[x]=ans;
    		if(x==fa[x]) break;
    		ans=max(ans,val[x]);
    		x=fa[x];
    	}
    	while(1) {
    		if(v[y]) {ans1=max(ans1,v[y]);break;}
    		if(y==fa[y]) break;
    		ans1=max(ans1,val[y]);
    		y=fa[y];
    	}
    	return ans1;
    }
    int cs;
    int main() {
    	while(scanf("%d%d",&n,&m)==2) {
    		if(cs++) putchar('
    ');
    		for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
    		for(int i=1;i<=m;i++) {
    			e[i].u=read(),e[i].v=read(),e[i].w=read();
    		}	
    		sort(e+1,e+1+m);
    		for(int i=1;i<=m;i++) 
    			merge(e[i].u,e[i].v,e[i].w);
    		int q;	
    		q=read();
    		int x,y;
    		while(q--) {
    			x=read(),y=read();
    			printf("%d
    ",query(x,y));
    		}
    	}
    	return 0;
    }
    
    
    

    货车运输

    #include <cstdio>
    #include <cstring>
    #include <utility>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=2e5+5;
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    struct Edge{
    	int u,v,w;
    	bool operator < (const Edge &x) const {
    		return w>x.w;
    	}
    }e[N];
    int hd[N],tot;
    int n,m,fa[N],siz[N],val[N],dep[N];
    int find(int x) {
    	if(x==fa[x]) return val[x]=0,x;
    	int dad=find(fa[x]);
    	dep[x]=dep[fa[x]]+1;
    	return dad;
    }
    void merge(int x,int y,int z) {
    	x=find(x),y=find(y);
    	if(x==y) return;
    	if(siz[x]<siz[y]) swap(x,y);
    	fa[y]=x;val[y]=z;
    	if(siz[x]==siz[y]) siz[x]++;
    }
    int query(int x,int y) {
    	if(find(x)!=find(y)) return -1;
    	int ans=0x3f3f3f3f;
    	while(x!=y) {
    		if(dep[x]<dep[y]) swap(x,y);
    		ans=min(ans,val[x]);
    		x=fa[x];
    	}
    	return ans;
    }
    int cs;
    int main() {
    	n=read();m=read();
    	for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
    	for(int i=1;i<=m;i++) {
    		e[i].u=read(),e[i].v=read(),e[i].w=read();
    	}	
    	sort(e+1,e+1+m);
    	for(int i=1;i<=m;i++) 
    		merge(e[i].u,e[i].v,e[i].w);
    	int q;	
    	q=read();
    	int x,y;
    	while(q--) {
    		x=read(),y=read();
    		printf("%d
    ",query(x,y));
    	}
    	return 0;
    }
    

    城市猎人

    提交 http://192.168.14.142/problem/57

    对于第i天 ,设x=m-i+1 ,则会有 $$ x1,x2...x*j $$这些边相连,且边权为 i ,如果做过按秩合并的题,那么可一眼看出。。。接下来就是按秩合并咯

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=100010;
    inline int read() {
    	int x=0;char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x;
    }
    int n,m,q;
    int fa[N],siz[N],dep[N];
    int val[N];
    inline int find(int x) {
    	if(fa[x]==x) return dep[x]=0,x;
    	int dad=find(fa[x]);
    	dep[x]=dep[fa[x]]+1;
    	return dad;
    }
    void merge(int x,int y,int z) {
    	x=find(x),y=find(y);
    	if(x!=y) {
    		if(siz[x]<siz[y]) swap(x,y);
    		fa[y]=x;val[y]=z;
    		if(siz[x]==siz[y])  siz[x]++;		
    	}
    }
    int main() {
    	n=read();m=read();q=read();
    	for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    	for(int i=1;i<=m;i++) {
    		int x=m-i+1;
    		for(int j=1;x*j+x<=n;j++) merge(x*j,x*j+x,i);
    	}
    	while(q--) {
    		int x=read(),y=read(),ans=0;
    		find(x),find(y);
    		if(dep[x]<dep[y]) swap(x,y);
    		while(dep[x]>dep[y]) ans=max(ans,val[x]),x=fa[x];
    		while(x!=y) {
    			ans=max(ans,val[x]);x=fa[x];
    			ans=max(ans,val[y]);y=fa[y];
    		}
    		printf("%d
    ",ans);
    	} 
    	
    	return 0;
    }
    
    
    

    冷战

  • 相关阅读:
    Kali linux installation instruction
    Source Insight(C/C++/Java编辑器)用法
    Wear OS软件安装指南
    外卖ERP管理系统(一)
    邮轮ERP系统
    使用CEfSharp 下载文件 弹出保存框 IDownloadHandler
    cefSharp通过js操控页面,含跨域操控
    C# Winform 未能加载文件或程序集"System.Data.SQLite"或它的某一个依赖项。试图加载格式不正确的程序
    无法将类型为“System.__ComObject”的 COM 对象强制转换为类类型“mshtml.HTMLInputElementClass
    COMBOBOX绑定DICTIONARY做为数据源
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13594073.html
Copyright © 2020-2023  润新知