[USACO16OPEN]Closing the Farm G (并查集、建图)

文章介绍了如何使用并查集和邻接链表解决一道关于图的连通性问题。在处理过程中,通过逆向增加节点来减少谷仓个数,动态维护连通块数量,判断是否全联通。代码实现中展示了并查集的查询和合并操作,以及邻接链表存储边的方法。
摘要由CSDN通过智能技术生成

(对本蒟蒻来说一道较难的题,记录一下)

题目链接

本题主要考察了 并查集、建图。

 思维点:题目中按顺序减少谷仓的个数可以视作逆向依次增加节点。

了解这些后可以开始做题。

Solution:

        1.基本的邻接链表,并查集函数和变量的添加。

        2.常规的用邻接链表存边。

        3.中间处理时,可以根据连通块的数量来判断是否是全联通。每次加入一个新的节点后,                先增加连通块的个数,后续处理时如果这个节点对应的边能够连接两个连通块(一条边能够            生效的前提是该条边的两个节点都要加入到图中,用vis[ ]来判断),那么就减去一个连通块            的数量,最后如果连通块的数量为1,那么就是全联通。

Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF (long long)2e+18
const int N=2e5+10;
const int mod=1e9;
typedef pair<int,int> PII;


int n,m;
int h[N],e[2*N],ne[2*N],idx;//邻接链表
int a[N];//倒序依次加入节点
int p[N];// 并查集
bool ans[N];
bool vis[N];
//加边
void add(int a,int b){
	e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
//并查集查询、合并操作
int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}
int merge(int x,int y){
	if(find(x)==find(y)) return 0;
	p[find(x)]=find(y);
	return 1;
}  

int main()
{
	ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    memset(h,-1,sizeof h);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		p[i]=i;
	}
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		add(x,y);add(y,x);
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int now=0;//当前连通块的数量
	for(int i=n;i>=1;i--){
		now++;//先看作加一个连通块,后面再进行处理
		int nn=a[i];
	//	p[nn]=nn;
		vis[nn]=1;
		for(int j=h[nn];j!=-1;j=ne[j]){
			int k=e[j];
			if(vis[k]==1){
				now-=merge(nn,k);
			}
		}
		if(now==1) ans[i]=1;
		else ans[i]=0;
	}
	for(int i=1;i<=n;i++){
		if(ans[i]) cout<<"YES";
		else cout<<"NO";
		cout<<'\n';
	}
}

Tips:

        这里借用了大佬用并查集数组p[ ]数组同时担当vis[ ]的作用的思路,同时merge函数同时做到了连通和判断是否连通。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值