(对本蒟蒻来说一道较难的题,记录一下)
本题主要考察了 并查集、建图。
思维点:题目中按顺序减少谷仓的个数可以视作逆向依次增加节点。
了解这些后可以开始做题。
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函数同时做到了连通和判断是否连通。