题目链接:https://www.luogu.com.cn/problem/P3144
题目大意
- 起初有n个谷仓有m条路径,问依次关闭n个谷仓之后,农场的联通情况如何(任意两个开放的谷仓之间可以相互到达)
- 谷仓关闭之后相应的路径也随着消失
- 范围 1≤N,M≤3000
思路
- 我们可以倒着思考,转换为谷仓从无到有的过程,依次添加谷仓,当联通块数为1时,表明联通,否则为分散
- 问的是关闭之前的联通块个数,那么反过来就是开放之后联通块的个数
- 求连通块的话,并查集是一个不错的选择
ac代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 3005;
int pre[maxn], vis[maxn], order[maxn], ans[maxn];
int n, m;
vector<int> v[maxn];
int find(int a){ // 查祖先节点
if(a == pre[a]) return a;
return pre[a] = find(pre[a]);
}
void merge(int a, int b){ //合并祖先节点
int x = find(a), y = find(b);
if(x != y) pre[x] = y;
}
int cal(){ //求连通块的个数
int cnt = 0;
for(int i = 1; i <= n; i ++){
if(vis[i]) //前提是开放的谷仓
cnt += (find(i) == i);
}
return cnt;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) {
pre[i] = i; //初始化祖先就是自己
}
for(int i = 1; i <= m; i ++){//存边
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i = 1; i <= n; i ++){
int x; scanf("%d", &x);
order[i] = x; //记录关闭顺序
vis[x] = 0; //先给他关了
}
for(int i = n; i >= 1; i --){ //倒着开放谷仓
int d = order[i];
vis[d] = 1; //开放这个谷仓
for(auto it : v[d]){ //遍历相关的边看看需不需要合并,两个谷仓都开放才能合并
if(vis[it]) merge(d, it);
}
ans[i] = cal(); //记录联通块个数
}
for(int i = 1; i <= n; i ++){
if(ans[i] == 1) puts("YES");//联通块个数是1说明全联通
else puts("NO");
}
return 0;
}