Description:
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。
Analysis:
历时三个月,终于做出来了。
很显然要用到并查集,由于破坏一个点后再找连通块会很麻烦,因此考虑逆向重建星球。每加入一个星球后判断与其相连的星球之前是否存在,如果不存在,则连通块数加一,否则不加。
May the force be with you.
Code
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 400095;
int fa[N],n,m,x,y,k,Cnt;
bool ruin[N];
int head[N],atk[N],ans[N],num_edge;
struct edge{
int x,y,next;
}e[N];
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
inline void add(int u,int v){
e[++num_edge].next = head[u];
e[num_edge].x = u;
e[num_edge].y = v;
head[u] = num_edge;
}
inline void merge(int a,int b) {
a = find(a);
b = find(b);
fa[a] = b;
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 0; i <= n;++i) fa[i] = i;
Cnt = n;
for(int i = 1; i <= m;++i) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
scanf("%d",&k);
for(int j = 1; j <= k;++j) {
scanf("%d",&atk[j]);
ruin[atk[j]] = 1;
}
Cnt = n - k;
// final status
for(int i = 1; i <= (m << 1);++i) {
if( !ruin[e[i].x] && !ruin[e[i].y] && find(e[i].x) != find(e[i].y) ) {
Cnt--;
merge(e[i].x,e[i].y);
}
}
ans[k + 1] = Cnt;
for(int j = k;j >= 1;--j){
int u = atk[j];
ruin[u] = 0;
Cnt++;
for(int i = head[u];i;i = e[i].next){
if(find(u) != find(e[i].y) && !ruin[e[i].y] ){
Cnt--;
merge(u,e[i].y);
}
}
ans[j] = Cnt;
}
for(int i = 1;i <= k + 1;++i){
printf("%d
",ans[i]);
}
return 0;
}