并查集题目(路径压缩、扩展域并查集、带权并查集、二维转一维并查集、逆向思维并查集)

目录

P2814 家谱

P1955 [NOI2015] 程序自动分析

P2256 一中校运会之百米跑    (并查集结合map)

P3144 [USACO16OPEN]Closing the Farm S

P6121 [USACO16OPEN]Closing the Farm G

P1197 [JSOI2008] 星球大战

P5092 [USACO04OPEN]Cube Stacking (带权并查集)

P5877 棋盘游戏

 P8686 [蓝桥杯 2019 省 A] 修改数组

P1840 Color the Axis

Supermarket

P2949 [USACO09OPEN]Work Scheduling G

P3402 可持久化并查集

P2814 家谱

#include <bits/stdc++.h>
using namespace std;
int len, cnt, father, fa[50010];
string s;
map<string, int> m;
string a[50010];
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else return fa[x]=find(fa[x]);
}
int main()
{
	while(cin>>s && s[0]!='$'){
		string name="";
		len=s.length();
		for(int i=1; i<len; ++i){
			name+=s[i];
		}
		if(s[0]=='#'){
			if(m.count(name)!=0){
				father=m[name];
				continue;
			}
			cnt++;
			m[name]=cnt;
			a[cnt]=name;
			fa[cnt]=cnt;
			father=cnt;
		}
		else if(s[0]=='+'){
			if(m.count(name)!=0){
				int id=m[name];
				fa[id]=father;
				continue;
			}
			cnt++;
			m[name]=cnt;
			a[cnt]=name;
			fa[cnt]=father;
		}
		else if(s[0]=='?'){
			int id=m[name];
			cout << name << " " << a[find(id)] << endl;
		}
	}
	return 0;
}

P1955 [NOI2015] 程序自动分析

并查集和map(哈希)结合起来的一道好题

#include <bits/stdc++.h>
using namespace std;
int t, n, fa[1000010], cnt;
bool flag;
map<int, int> m;
struct node
{
	int x, y, z;
}limit[1000010];
bool cmp(node a, node b)
{
	return a.z>b.z;
}
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	if(fu!=fv){
		fa[fu]=fv;
	}
}
int main()
{
	scanf("%d", &t);
	while(t--){		//10
		scanf("%d", &n);
		m.clear();
		flag=true;
		for(int i=1; i<=n; ++i){		//1e6
			scanf("%d %d %d", &limit[i].x, &limit[i].y, &limit[i].z);
		}
		sort(limit+1, limit+n+1, cmp);	//1e6*20 
		for(int i=1; i<=n; ++i){		//1e6 
			int u=limit[i].x;
			int v=limit[i].y;
			int e=limit[i].z;
			if(m.count(u)==0){
				cnt++;
				fa[cnt]=cnt;	//初始化并查集	
				m[u]=cnt;		//将大范围的整数u映射成小范围的整数cnt 
			}
			if(m.count(v)==0){
				cnt++;
				fa[cnt]=cnt;	//初始化并查集
				m[v]=cnt;		//将大范围的整数v映射成小范围的整数cnt 
			}
			if(e==1){			//合并 
				merge(m[u], m[v]);
			} 
			else{				//判断u和v的祖先是否相同 
				int fu=find(m[u]);
				int fv=find(m[v]);
				if(fu==fv){
					flag=false;
					printf("NO\n");
					break;
				}
			}
		}
		if(flag){
			printf("YES\n");	
		}
	}
	return 0;
}

P2256 一中校运会之百米跑    (并查集结合map)

#include <bits/stdc++.h>
using namespace std;
const int N=20010; 
int n, m, k, fa[N], u, v, fu, fv;
string name1, name2;
map<string, int> asd;
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		fa[i]=i;
		cin >> name1;
		asd[name1]=i;
	}
	while(m--){
		cin >> name1 >> name2;
		u=asd[name1];
		v=asd[name2];
		merge(u, v);
	}
	scanf("%d", &k);
	while(k--){
		cin >> name1 >> name2;
		u=asd[name1];
		v=asd[name2];
		if(find(u)==find(v)){
			printf("Yes.\n");
		}
		else{
			printf("No.\n");
		}
	}
	return 0;
}

P3144 [USACO16OPEN]Closing the Farm S

并查集倒着做合并, AC

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[3010], fa[3010], cnt;
bool in[3010], vis[3010];
string ans[3010];
vector<int> a[3010];
int find(int x)
{
	if(x==fa[x]){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &u, &v);
		a[u].push_back(v);
		a[v].push_back(u);
	}
	for(int i=1; i<=n; ++i){
		fa[i]=i;
		scanf("%d", &close[i]);	//要关掉的谷仓编号 
	}
	for(int i=n; i>=1; --i){
		u=close[i];
		in[u]=true;		//该点加入到图中
		for(int j=0; j<a[u].size(); ++j){
			v=a[u][j];
			if(in[v]){
				merge(u, v);	
			}
		} 
		memset(vis, 0, sizeof(vis));
		cnt=0;
		for(int j=1; j<=n; ++j){
			if(in[j] && !vis[find(j)]){
				vis[find(j)]=true;
				cnt++;
				if(cnt>1){
					break;
				}
			}
		}
		if(cnt>1){
			ans[i]="NO";
		}
		else{
			ans[i]="YES";
		}
	}
	for(int i=1; i<=n; ++i){
		cout << ans[i] << endl;
	}
	return 0;
}

暴力做法,50分, TLE

#include <bits/stdc++.h>
using namespace std;
int n, m, a[3010][3010], u, v, start[3010], close, vis[3010], cnt;
queue<int> q;
bool check(int x)
{
	//多测清空 
	cnt=0;
	memset(vis, 0, sizeof(vis));
	while(!q.empty()){
		q.pop();
	}
	//找起点 
	for(int i=1; i<=n; ++i){
		if(start[i]==false){
			q.push(i);
			vis[i]=true;
			cnt=1;
			break;		
		}
	}
	//遍历 
	while(!q.empty()){
		if(cnt==n-x){
			return true;
		}
		u=q.front();
		q.pop();
		for(int i=1; i<=n; ++i){
			if(a[u][i] && !vis[i]){
				vis[i]=true;
				cnt++;
				q.push(i);
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &u, &v);
		a[u][v]=a[v][u]=1;	//建边 
	}
	for(int i=1; i<=n; ++i){
		if(check(i-1)){		//关掉i-1个谷仓	
			printf("YES\n");
		}
		else{
			printf("NO\n");
		}
		scanf("%d", &close);	//要关掉的谷仓编号 
		start[close]=true;		//标记为关闭状态 
		for(int j=1; j<=n; ++j){	//断边 
			a[close][j]=a[j][close]=0;	
		}
	}
	return 0;
}

P6121 [USACO16OPEN]Closing the Farm G

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[200010], fa[200010], cntedge, cntnode;
bool in[200010], vis[200010];
string ans[200010];
vector<int> a[200010];
int find(int x)
{
	if(x==fa[x]){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	while(m--){
		scanf("%d %d", &u, &v);
		a[u].push_back(v);
		a[v].push_back(u);
	}
	for(int i=1; i<=n; ++i){
		fa[i]=i;
		scanf("%d", &close[i]);	//要关掉的谷仓编号 
	}
	//O(M)
	for(int i=n; i>=1; --i){
		u=close[i];
		cntnode++;
		in[u]=true;		//该点加入到图中
		for(int j=0; j<a[u].size(); ++j){
			v=a[u][j];
			if(in[v] && find(u)!=find(v)){
				merge(u, v);	
				cntedge++;
			}
		} 
		if(cntedge+1<cntnode){
			ans[i]="NO";
		}
		else{
			ans[i]="YES";
		}
	}
	for(int i=1; i<=n; ++i){
		cout << ans[i] << endl;
	}
	return 0;
}

P1197 [JSOI2008] 星球大战

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, k, close[400010], fa[400010], cntedge, cntnode, asd;
int ans[400010];
bool in[400010], vis[400010];
vector<int> a[400010];
int find(int x)
{
	if(x==fa[x]){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=0; i<n; ++i){
		fa[i]=i;
	}
	while(m--){
		scanf("%d %d", &u, &v);
		a[u].push_back(v);
		a[v].push_back(u);
	}
	scanf("%d", &k); 
	for(int i=1; i<=k; ++i){
		scanf("%d", &close[i]);	//要关掉的星球编号
		vis[close[i]]=true;		//如果某个星球始终没有被关掉, 则vis为false 
	}
	asd=n-k;	//假设这些点都没相连, 联通块数量最多为n-k 
	//逆向思维处理, 反向建设相当于正向摧毁 
	//先处理k座星球全部被摧毁后的情况 
	for(int i=0; i<n; ++i){
		if(!vis[i]){	//i始终没有被摧毁 
			in[i]=true;	//i在图里 
			for(int j=0; j<a[i].size(); ++j){	
				v=a[i][j];	//枚举与i相连的点 
				if(!vis[v] && find(i)!=find(v)){	//如果该点也始终没有被摧毁, 并且有边相连 
					merge(i, v);	//合并 
					asd--;		//联通块数量减一 
				}
			}
		}
	}
	ans[k+1]=asd;
	//再逆向处理第i座星球被摧毁前的情况
	for(int i=k; i>=1; --i){
		//把第i座星球加入到图中 
		u=close[i];
		asd++;
		in[u]=true;		//该点加入到图中
		for(int j=0; j<a[u].size(); ++j){
			v=a[u][j];
			if(in[v] && find(u)!=find(v)){	//连一条边, 没有形成环 
				merge(u, v);	//合并 
				asd--;			//联通块数量-- 
			}
		} 
		ans[i]=asd;
	}
	for(int i=1; i<=k+1; ++i){
		cout << ans[i] << endl;
	}
	return 0;
}

P5092 [USACO04OPEN]Cube Stacking (带权并查集)

#include <bits/stdc++.h>
using namespace std;
const int N=30010;
int p, fa[N], dis[N], size[N], u, v;
char opt; 
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	int root=find(fa[x]);		//找到自己祖宗结点的祖宗 
	dis[x]+=dis[fa[x]];			//更新到自己祖宗的祖宗(root)的距离, 等于自己到当前祖宗结点的距离+当前祖宗结点到它的祖宗结点的距离 
	return fa[x]=root;			//直接链到最终的祖宗 
}
void merge(int u, int v)
{
	int fu, fv; 
	fu=find(u);
	fv=find(v);
	if(fu==fv)	return;
	fa[fu]=fv;
	dis[fu]+=size[fv];		//距离先存在祖宗结点fu这里, 后面需要用的时候find去更新 
	size[fv]+=size[fu];		//祖宗保存元素个数 
} 
int main()
{
	scanf("%d", &p);
	for(int i=1; i<=N; ++i){	//并查集初始化, dis[i]默认为0 
		fa[i]=i;	
		size[i]=1;
	}
	while(p--){
		cin >> opt;
		if(opt=='M'){			//将包含 u 的立方柱移动到包含 v 的立方柱上
			scanf("%d %d", &u, &v);		
			merge(u, v); 
		}
		else if(opt=='C'){		//统计含 u 的立方柱中,在 u 下方的方块数目。
			scanf("%d", &u);
			find(u);
			printf("%d\n", dis[u]);
		}
	}
	return 0;
}

P5877 棋盘游戏

二维转一维,用并查集处理

#include <bits/stdc++.h>
using namespace std;
int n, m, c, x, y, a[510][510], fa[250010], id, asd;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int find(int x)
{
	if(fa[x]==x){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; ++i){
		for(int j=1; j<=n; ++j){
			id=(i-1)*n+j;
			fa[id]=id;
		}
	}
	memset(a, -1, sizeof(a));
	while(m--){
		scanf("%d %d %d", &c, &x, &y);
		asd++;
		id=(x-1)*n+y;
		a[x][y]=c;
		for(int i=1; i<=4; ++i){
			int nx=x+mx[i];
			int ny=y+my[i];
			if(nx>=1 && nx<=n && ny>=1 && ny<=n && a[nx][ny]==c && find(id)!=find((nx-1)*n+ny)){
				merge(id, (nx-1)*n+ny);
				asd--;
			}
		}
		printf("%d\n", asd);
	}
	return 0;
}

 P8686 [蓝桥杯 2019 省 A] 修改数组

路径压缩,删除中间的数,相当于区间合并

#include <bits/stdc++.h>
using namespace std;
int n, fa[2000010], a[100010];
bool vis[2000010];
int find(int x)
{
	if(fa[x]==x){
		return x;
	}	
	else{
		return fa[x]=find(fa[x]);
	}
} 
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	fa[fu]=fv;
}
int main()
{
	scanf("%d", &n);
	for(int i=1; i<=2000000; ++i){
		fa[i]=i;
	}
	for(int i=1; i<=n; ++i){
		scanf("%d", &a[i]);
	}
	for(int i=1; i<=n; ++i){
		if(find(a[i])==a[i]){		//没出现 
			cout << a[i] << " ";
			merge(a[i], a[i]+1);
		}
		else{	//出现过 
			cout << fa[a[i]] << " ";
			merge(fa[a[i]], fa[a[i]]+1);
		}
	}
	return 0;
}

P1840 Color the Axis

并查集进行区间合并

#include <bits/stdc++.h>
using namespace std;
int n, m, fa[200010], l, r, asd;
int find(int x)
{
	if(x==fa[x]){
		return x;
	}
	else{
		return fa[x]=find(fa[x]);
	}
}
void merge(int u, int v)
{
	int fu=find(u);
	int fv=find(v);
	//让大的当父亲, 不然会TLE 
	fa[fu]=fv;
}
int main()
{
	scanf("%d %d", &n, &m);
	asd=n;
	//注意细节, 需要初始化到n+1, 不然TLE 
	for(int i=1; i<=n+1; ++i){
		fa[i]=i;
	}	
	while(m--){
		scanf("%d %d", &l, &r);
		for(int i=l; i<=r; i=find(i)){
			//如果该点没有被涂过 
			if(find(i)==i){		
				asd--;		//答案-- 
				//该点被涂过了, 将该点和下一个点合并,
				//当下次再找到该点时, 直接通过find(i)跳到下一个没有被涂的点 
				merge(i, i+1);		 
			}	
		}
		printf("%d\n", asd);	
	}
	return 0;
}

Supermarket

Supermarket - 洛谷

P2835 刻录光盘

P1333 瑞瑞的木棍

P2391 白雪皑皑

P2949 [USACO09OPEN]Work Scheduling G

P3402 可持久化并查集

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ypeijasd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值