题意描述

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。

某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。

之后他们在各个星球之间建立了以太隧道,这些星球通过特殊的以太隧道互相直接或间接地连接。

但好景不长,很快帝国又重新造出了他的超级武器。

凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。

由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。

现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。


输入格式

第一行包含两个整数$n$ , $m$,分别表示星球的数目和以太隧道的数目。

接下来的 $m$ 行,每行包括两个整数 $x$, $y$ ,表示星球 $x$ 和星球 $y$ 之间有 “以太” 隧道,可以直接通讯。

接下来的一行为一个整数 $k$ ,表示将遭受攻击的星球的数目。

接下来的 $k$ 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。


输出格式

共$k + 1$行,第一行是开始时星球的连通块个数。

接下来的 $k$ 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。


Input & Output ‘s examples

Input ‘s eg

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7

Output ‘s eg

1
1
1
2
3
3

数据范围和约定

对于$100\%$的数据,$0 < m < 200000$ , $0 < n < 2\times m$,$0 < x , y < n$

星球编号为$0$到$n - 1$


分析

一道并查集好题。

题目给出的删除操作并不容易实现,因此,我们把删除操作转换为重建操作。

这样,题目就由$1$个连通块拆为多个连通块变成了由多个连通块合成$1$个连通块。

对于每一次操作,我们建立一个新的节点,然后使用并查集来判断其连通性,并将其记录到答案中。

输出时倒序输出即可。


Code[Accepted]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<deque>
#include<vector>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cctype>
#include<cmath>

#define N 1000001
#define M 1000001

using namespace std;

int pre[N];
int find(int root){
if(pre[root] == root){
return root;
}
else{
return pre[root] = find(pre[root]);
}
}

bool pd(int a , int b){
if(find(a) == find(b)){
return true;
}
else{
return false;
}
}

void merge(int a , int b){
a = find(a);
b = find(b);
if(a != b){
pre[a] = b;
}

}

struct Edge{
int from;
int to;
int last;
}edge[M];

int edge_number;
int hhead[N];

void add_edge(int from , int to){
edge[++ edge_number].to = to;
edge[edge_number].from = from;
edge[edge_number].last = head[from];
head[from] = edge_number;

}

int n , m;
int x , y , k;
int ans[N];

void Start(){
for(int i = 0; i <= n; i ++){
pre[i] = i;
}
}

int boom[N];
bool Broken[N];



int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
Start();
for(int i = 0; i < m; i ++){
cin >> x >> y;
add_edge(x , y);
add_edge(y , x);
}
cin >> k;
for(int i = 1; i <= k; i ++){
cin >> boom[i];
Broken[boom[i]] = true;
}
int number = n - k;
for(int i = 1; i <= 2 * m; i ++){
if(!Broken[edge[i].from] && !Broken[edge[i].to] && pd(edge[i].from , edge[i].to) == false){
number --;
merge(edge[i].from , edge[i].to);
}
}
ans[k + 1] = number;
for(int i = k; i >= 1; i --){
number ++;
Broken[boom[i]] = false;
for(int j = head[boom[i]] ; j ; j = edge[j].last){
if(!Broken[edge[j].to] && pd(boom[i] , edge[j].to) == false){
number --;
merge(boom[i] , edge[j].to);
}
}
ans[i] = number;
}
for(int i = 1; i <= k + 1; i ++){
cout << ans[i] << "\n";
}
return 0;
}

THE END