• 并查集的妙用


    (cal{A}.)最简单的并查集

    【图论_并查集】 [Luogu p1551] 亲戚

    并查集最简单的思路,使用了其“并”与“查”的功能。

    Luogu P3367 【模板】并查集

    并查集裸题;

    [Luogu 2078] 朋友

    “并”“查”+计数

    可以在每次合并时,以编号小的作为“父亲”,编号大的作为儿子,最后取小明和小红里“儿子”较小的一个

    (code:)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,m,p,q,ans;
    int fa[20050],cnt[20050];
    
    
    int find(int x) {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    int main() {
    	n=read();m=read();p=read();q=read();
    	for(int i=1;i<=m+n;i++)
    		fa[i]=i,cnt[i]=1;
    	int x,y,fx,fy;
    	for(int i=1;i<=p;i++) {
    		x=read();
    		y=read();
    		fx=find(x);
    		fy=find(y);
    		if(fx!=fy) {
    			int Fx=min(fx,fy);
    			int Fy=max(fx,fy);
    			fa[Fy]=Fx;
    			cnt[Fx]+=cnt[Fy];
    		}
    	}
    	for(int i=1;i<=q;i++) {
    		x=read();
    		y=read();
    		x=-x; x+=n;
    		y=-y; y+=n;
    		fx=find(x); fy=find(y);
    		if(fx!=fy) {
    			int Fx=min(fx,fy);
    			int Fy=max(fx,fy);
    			fa[Fy]=Fx;
    			cnt[Fx]+=cnt[Fy];
    		}		
    	}
    	ans=min(cnt[1],cnt[1+n]);
    	printf("%d",ans);
    	return 0;
    }
    

    Luogu P1536 村村通

    输入两个村庄后就把它们连起来,输入完毕后用i从1循环到n,所以如果i的父亲为它本身的话(它是祖先,它没有父亲),ans+1。答案要减1,因为三个点中只需用两条线连接,无需用三条线连接。(原blog)

    (cal{B}.)带权并查集

    Luogu P1196NOI2002 银河英雄传说

    (code :)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    inline char Getchar() {
    	char a;
    	do {
    		a=getchar();
    	}while(a!='M'&&a!='C');
    	return a;
    }
    
    struct node {
    	int head,dist,cnt;
    }boat[30010];
    
    int T;
    int fa[30010];
    
    int find(int x) {
    	if(fa[x]==x) return x;
    	int k=fa[x];
    	fa[x]=find(fa[x]);
    	boat[x].dist+=boat[k].dist;
    	boat[x].cnt=boat[k].cnt;
    	return fa[x];
    }
    
    void Union(int A,int B) {
    	int x=find(A),y=find(B);
    	fa[x]=y;
    	boat[x].dist+=boat[y].cnt;
    	boat[y].cnt+=boat[x].cnt;
    	boat[x].cnt=boat[y].cnt;
    }
    
    int main() {
    	T=read();
    	char c;
    	int a,b;
    	for(int i=1;i<=30000;i++) {
    		boat[i].head=i;
    		boat[i].dist=0;
    		boat[i].cnt=1;
    		fa[i]=i;
    	}
    	while(T--) {
    		c=Getchar();
    		a=read();
    		b=read();
    		if(c=='M') {
    			Union(a,b);
    		}
    		else {
    			int x=find(a);
    			int y=find(b); 
    			if(x!=y)
    				printf("-1
    ");
    			else 
    				printf("%d
    ",abs(boat[a].dist-boat[b].dist)-1);
    		}
    	}
    	return 0;
    }
    

    (cal{C}.)最小生成树

    [Luogu 2820] 局域网

    要使除去的(sum f(i,j))最大,反向思考,就是使剩下的边的(sum f(i,j))最小,然后用(Sum-sum limits^{rest} f(i,j))即为答案;

    可以想到最小生成树

    (code:)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,k,ans,m,num,tre;
    int fa[20050];
    struct node {
    	int u,v,w;
    }e[3000];
    
    int find(int x) {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    bool cmp(node x,node y) {
    	return x.w<y.w;
    }
    
    int main() {
    	n=read();k=read();
    
    	for(int i=1;i<=n;i++)
    		fa[i]=i;
    
    	for(int i=1;i<=k;i++) {
    		e[i].u=read();
    		e[i].v=read();
    		e[i].w=read();
    		num+=e[i].w;
    	}
    
    	sort(e+1,e+k+1,cmp);
    	
    	for(int i=1,u,v,w;i<=k;i++) {
    		if(m>=n) break;
    		u=e[i].u;
    		v=e[i].v;
    		w=e[i].w;
    		int fu=find(u);
    		int fv=find(v);
    		if(fu!=fv) {
    			fa[fu]=fv;
    			tre+=w;
    			m++;
    		}
    	}
    
    	ans=num-tre;
    	printf("%d",ans);
    	return 0;
    }
    

    Luogu P1547 [USACO05MAR] Out fo Hay S

    最小生成树(Kruskal),取最小生成树中最长的边

    (code:)

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<cmath>
    #include<string>
    #define pa pair<int,int>
    #define inf 2147483647
    
    using namespace std;
    
    inline int read(){
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,m,maxx,cnt;
    long long ans;
    int fa[2010];
    struct node{
    	int x,y,l;
    }g[10010];
    
    int find(int x){
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    bool cmp(node a,node b){
    	return a.l<b.l;
    }
    
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=m;i++){
    		g[i].x=read();
    		g[i].y=read();
    		g[i].l=read();
    	}
    	sort(g+1,g+m+1,cmp);
    	for(int i=1;i<=n;i++) fa[i]=i;
    	int fv,fu;
    	for(int i=1;i<=m&&cnt<=n-1;i++){
    		fv=find(g[i].x);fu=find(g[i].y);
    		if(fv==fu) continue;
    		fa[fu]=fv;
    		cnt++;
    		maxx=max(maxx,g[i].l);
    	}
    	printf("%d",maxx);
    	return 0;
    }
    

    [Luogu P1396] 营救

    (Kruskal)最小生成树

    (s)(t)已经相连了,停止并查集,输出此时的最大拥挤度

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,m,s,t,ans;
    struct node {
    	int u,v,w;
    }e[40010];
    int fa[10010];
    
    bool cmp(node x,node y) {
    	return x.w<y.w;
    }
    
    int find(int x) {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    int main() {
    	n=read(); m=read();
    	s=read(); t=read();
    	for(int i=1;i<=m;i++) {
    		e[i].u=read();
    		e[i].v=read();
    		e[i].w=read();
    	}
    	for(int i=1;i<=n;i++)
    		fa[i]=i;
    	sort(e+1,e+m+1,cmp);
    	int k=0;
    	for(int i=1,u,v,w;i<=m;i++) {
    		if(k>=n) break;
    		u=e[i].u;v=e[i].v;w=e[i].w;
    		int fu=find(u),fv=find(v);
    		if(fu!=fv) {
    			ans=max(ans,w);
    			fa[fu]=fv;
    			k++;
    		}
    		
    		if(find(s)==find(t))
    			break;
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    Luogu P1550 [USACO08OCT]Watering Hole G

    新建一个水源节点,与所有田相连,权值为(W_i)

    最小生成树(Kruskal)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read() {
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,ecnt,ans;
    struct node {
    	int u,v,w;
    }e[100110];
    int fa[310],w[310];
    
    bool cmp(node x,node y) {
    	return x.w<y.w;
    }
    
    int find(int x) {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    int main() {
    	n=read();
    	for(int i=1,w;i<=n;i++) {
    		w=read();
    		e[++ecnt].u=i;
    		e[ecnt].v=n+1;
    		e[ecnt].w=w;
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1,a;j<=n;j++) {
    			a=read();
    			if(i!=j) {
    				++ecnt;
    				e[ecnt].u=i;
    				e[ecnt].v=j;
    				e[ecnt].w=a;
    			}
    		}
    	for(int i=1;i<=n;i++)
    		fa[i]=i;
    	sort(e+1,e+ecnt+1,cmp);
    	int k=0;
    	for(int i=1,u,v,w;i<=ecnt;i++) {
    		if(k>=n) break;
    		u=e[i].u;v=e[i].v;w=e[i].w;
    		int fu=find(u),fv=find(v);
    		if(fu!=fv) {
    			ans+=w;
    			fa[fu]=fv;
    			k++;
    		}
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    [Luogu P1111] 修复公路

    最小生成树板子??

    最终判断是否所有点都在生成树内((cnt==n-1?)),输出最小生成树(max)

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<queue>
    #include<cmath>
    #include<string>
    #define pa pair<int,int>
    #define inf 2147483647
    
    using namespace std;
    
    inline int read(){
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    int n,m,maxx,cnt;
    long long ans;
    int fa[1010];
    struct node{
    	int x,y,l;
    }g[100010];
    
    int find(int x){
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x]; 
    }
    
    bool cmp(node a,node b){
    	return a.l<b.l;
    }
    
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=m;i++){
    		g[i].x=read();
    		g[i].y=read();
    		g[i].l=read();
    	}
    	sort(g+1,g+m+1,cmp);
    	for(int i=1;i<=n;i++) fa[i]=i;
    	int fv,fu;
    	for(int i=1;i<=m&&cnt<=n-1;i++){
    		fv=find(g[i].x);fu=find(g[i].y);
    		if(fv==fu) continue;
    		fa[fu]=fv;
    		cnt++;
    		maxx=max(maxx,g[i].l);
    		if(cnt==n-1) {
    			printf("%d",maxx);
    			return 0;
    		}
    	}
    	if(cnt==n-1){
    		printf("%d",maxx);
    		return 0;
    	}
    	else printf("-1");
    	return 0;
    }
    

    Luogu P1546 最短网络 Agri-Net

    最小生成树裸题?(Kruskal)

    #include<bits/stdc++.h>
    
    using namespace std;
    
    
    inline int read(){
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    struct edge{
        int from,to,dis;
    }g[500001];
    bool cmp(edge a,edge b){
        return a.dis<b.dis;
    }
    int fa[200001];
    int find_father(int a){
        if(fa[a]==a)return a;
        fa[a]=find_father(fa[a]);
        return fa[a];
    }
    int n,m,cnt,x;
    long long ans;
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++){
    			x=read();
    			if(x&&i<j) g[++m].from=i,g[m].to=j,g[m].dis=x;
    		}
    	}
        for(int i=1;i<=n;i++) fa[i]=i;
        sort(g+1,g+m+1,cmp);
        for(int i=1;i<=m&&cnt<=n-1;i++){
            int fu=find_father(g[i].from),fv=find_father(g[i].to);
            if(fu==fv)continue;
            fa[fu]=fv;
            cnt++;
            ans+=g[i].dis;
        }
        if(cnt==n-1){
            cout<<ans<<endl;
        }else{
            cout<<"No Solution.
    ";
        }
    }
    

    (cal{D}.)脑洞并查集

    (frak {a}.)奇怪二维并查集

    【一本通1347】格子游戏

    (frak {b}.)奇怪脑洞

    Luogu p2456 二进制方程

    Luogu P1197 [JSOI2008]星球大战

    反过来考虑,一个一个星球往上加

    把所有询问离线下来,倒着做。

    一个整理

  • 相关阅读:
    空间解析几何与向量代数(复习笔记)
    OpenGL数据类型
    Symbian c++ 调用标准C产生内存泄露
    symbian c++调用标准c函数方法
    (转载)一篇对理解OpenGL的描述的文章
    (个人摘要)make工具的用法
    (转载)哈佛大学凌晨4点半的景象
    网页抓取中的debug 问题记录
    split 使用
    三、抽象工厂(Abstract Factory)模式
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/12631138.html
Copyright © 2020-2023  润新知