[LuoGu]P1197-并查集求连通块
tips:
1.记录输入的哪些信息,是边读入,边处理还是都读入,后处理。
2.倒着恢复,插入一个新点,用并查集判断是否连通。
//#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<utility>
using namespace std;
const int maxN=400010;
const int maxM=200010;
vector<int> G[maxN];//邻接表
vector<pair<int,int> > e;//边集
int tmpK[maxN];
bool tag[maxN];
int ans[maxN];
int p[maxN];
int r[maxN];
int n,m;
int x,y;
void init(int x){
for(int i=0;i<n;i++){
p[i]=i;
r[i]=0;
}
}
int find(int x){
if(p[x]==x) return x;
else return p[x]=find(p[x]);
}
void unite(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx==fy) return;
if(r[fx]< r[fy]){
p[fx]=fy;
}
else{
p[fy]=fx;
if(r[fx] == r[fy]) r[fx]++;
}
}
bool same(int x, int y){
return find(x) == find(y);
}
int main(){
cin>>n>>m;
init(n);
for(int i=0;i<m;i++){
cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
e.push_back(make_pair(x,y));
}
int k;
cin>>k;
for(int i=0;i<k;i++){
cin>>tmpK[i];
tag[tmpK[i]]=1;
}
int s=e.size();
for(int i=0;i<s;i++){
int x=e[i].first;
int y=e[i].second;
if(!tag[x] && !tag[y]){
unite(x,y);
}
}
//统计全破坏后的连通块
int cur_ans=0;
for(int i=0;i<n;i++){
if(!tag[i]&& find(i)==i){
cur_ans++;
}
}
//倒着恢复被破坏的点,先假设刚恢复的点独自成块再判断恢复的点是否和其他块连通
for(int i=k-1;i>=0;i--){
ans[i]=cur_ans;
int cur_K=tmpK[i];
tag[cur_K]=0;
cur_ans++;
if(G[cur_K].size()){
for(int j=0; j<G[cur_K].size(); j++){
if(!tag[G[cur_K][j]]){
if(!same(cur_K,G[cur_K][j])){
cur_ans--;
unite(cur_K,G[cur_K][j]);
}
}
}
}
}
cout<<cur_ans<<endl;
for(int i=0;i<k;i++){
cout<<ans[i]<<endl;
}
return 0;
}
ps:
日常不练习,就会手生。
理解会随着时间加深,看待的角度也会变化,比如刚开始时是用搜索(dfs)求连通块的,现在也可以从并查集这个角度看。
之前有在思考计算机建立的基础,数学基础、硬件基础;计算机语言的设计方面的东西(mit、南大)。
多思考本质、基础。比如之前思考的计算机语言的三大逻辑语句就可以编程处理问题了?
ref:https://blog.csdn.net/KIKO_caoyue/article/details/84073688