都在代码里了!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxm = 200005 ;
const int maxn = 400005 ;
int n,m,head[maxn],top,father[maxn],k,ans[maxn],dam[maxn],num,topk; //dam[i]记为 第 i 次摧毁的是哪个点 ans[]记录某次摧毁后的联通块(这里我是倒着来存的)
bool vis[maxn]; //vis[i]记录的是 i 点是否被摧毁
struct edge{//这是邻接表的图
int next,to = -1; //注意 to=-1 这一点由于此图是从 0 - n-1 而这里to的初值原本为 0 ,若不改为-1 再调用的时候就会多出一个 0 点
}e[maxm << 1];
struct edge_know {
int st,en;
}ek[maxm << 1]; //这是题目中所给的原图
void add_edge(int u,int v) { //加边操作
e[++top].to = v;
e[top].next = head[u] ;
head[u] = top ;
}
int find(int x) { //并查集的寻找 注意路径压缩
if(father[x] == x) return x;
return father[x] = find(father[x]) ;
}
void unionn(int r1,int r2) { //合并操作 个人比较喜欢写在函数里面
father[find(r1)] = find(r2) ;
}
int main() {
memset(vis,0,sizeof(vis)) ;
scanf("%d%d",&n,&m) ;
for(int i = 0;i < n ;++i) father[i] = i;
for(int i = 1;i <= m ;++i) {
int x,y;
scanf("%d%d",&x,&y) ;
ek[++topk].st = x; ek[topk].en = y; add_edge(x,y) ; //给原图加边 给邻接表加边
ek[++topk].st = y; ek[topk].en = x; add_edge(y,x) ;
}
scanf("%d",&k) ;
for(int i = 1;i <= k ;++i) {
int x ; scanf("%d",&x) ;
vis[x] = 1 ; dam[i] = x; //离线读入摧毁的点
}
for(int i = 1;i <= m * 2;++i) {
if(vis[ek[i].st] == 1 || vis[ek[i].en] == 1) continue; //判断是否改边由于点的摧毁而无价值
if(find(ek[i].st) != find(ek[i].en)) {
unionn(ek[i].st , ek[i].en) ;
}
} //这里做的是最后状态
num = 1;
for(int i = 0;i < n ;++i) if(father[i] == i && !vis[i]) ans[num]++; //最后状态的联通块
for(int i = k;i >= 1 ;--i) {
vis[dam[i]] = 0; //因为要把该点加入 因此取消该店的标记
int u = dam[i] ; num++ ;
ans[num] = ans[num - 1] + 1; //加入后的初值
for(int j = head[u]; j ;j = e[j].next) { //遍历每个边 每个点
if(vis[e[j].to] == 1) continue ; //判断
if(find(u) != find(e[j].to)) {
unionn(u,e[j].to) ; //加入后的联通块数-1
ans[num]--;
}
}
}
for(int i = num;i >= 1 ;--i) printf("%d\n", ans[i]) ; //由于是倒着存储 因此从num开始 循环到 1
return 0;
}