致敬老鹰乐队,播放上一曲Hotel California,边听边看。
题意:反叛军想要摧毁帝国的星球。帝国的星球之间有着以太作为连接,以次帝国的星球直接或者间接相连着。反叛军每摧毁一个星球,其星球就不复存在,其和其他星球的以太隧道也将断开。给出一个k 是反叛军将要摧毁的星球数目,然后依次告诉你这k个即将被摧毁的星球的编号。问每次叛军摧毁一个星球后,有多少个连通块,如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中。
起初,我是想先把图建立好以后,每次摧毁一个星球,就将这个星球(点)在图中抹掉,相连的边也擦除,然后跑Tarjan求连通分量。emmm...应该可行。来吧,仔细想想,这么做不会TLE吗?绝对会呀,题目的标签是并查集,这个思路不行。那怎么办,看到别人说,并查集是用来建立边与边之间关系的。是一种和谐的结构,摧毁边,并查集是做不来的。那么为什么不反过来想,摧毁一条边看成建立一条边,我们从最后一次询问,即最后一个星球被毁灭开始,建立边,一直到第一个要被摧毁的星球,期间我们求的连通块的个数反着输出就是答案了。
/*
* Do not go gentle into that good night
* ----Dylan Thomas
* Author: looooop
* Created Time: 2018年12月11日 星期二 19时45分44秒
* File Name: p1197.cpp
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <time.h>
using namespace std;
#define lson 2*i
#define rson 2*i+1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
#define MAX 400005
struct Edge{
int from;
int to;
int next;
};
int head[MAX];int fa[MAX];
int mark[MAX];int echo[MAX];
int attack[MAX];int ans[MAX];int tot;
Edge G[MAX];
int cnt;
int n,m;
int x,y;
int k;
void add(int u,int v){
cnt++;
G[cnt].from = u;
G[cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
int Find(int x){
if(x != fa[x])
return fa[x] = Find(fa[x]);
return x;
}
void init(){
for(int i = 0; i <= n; i++)
fa[i] = i;
MEM(head,0);
tot = n-k; //逆着并查集时,所有k个星球都被摧毁后初始化帝国的连通块个数
}
int main(int argc,char *argv[]) {
scanf("%d%d",&n,&m);
//init();
for(int i = 0; i <= n; i++)
fa[i] = i;
MEM(head,0);
for(int i = 1; i <= m; i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
scanf("%d",&k);
tot = n-k;
//init();
for(int i = 1; i <= k; i++){
int victim;
scanf("%d",&victim); //受害者,要被摧毁的星球
mark[victim] = 1; //mark标记,是否已被摧毁
attack[i] = victim; //victim,存在数组,方便后面逆着做
}
for(int i = 1; i <= m*2; i++){
if(mark[G[i].from]==0 and mark[G[i].to]==0){
if(Find(G[i].from) != Find(G[i].to))
tot--,fa[Find(G[i].from)] = fa[Find(G[i].to)];
}
}
ans[k+1] = tot;
for(int os = k; os >= 1; os--){
int st = attack[os];
mark[st] = 0;
tot++;
for(int i = head[st];i; i = G[i].next){
int v = G[i].to;
int Find_st = Find(st);
int Find_v = Find(v);
if(mark[G[i].to]==0 and fa[Find_st]!=fa[Find_v]){
tot--;
fa[Find_v] = fa[Find_st];
}
}
ans[os] = tot;
}
for(int i = 1; i <= k+1; i++)
printf("%d\n",ans[i]);
return 0;
}