JSOI2008 星球大战 洛谷P1197

Description

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


某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。


但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。


现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。


Input

输入文件第一行包含两个整数,N和M,分别表示星球的数目和以太隧道的数目。星球用0~N−1的整数编号。接下来的M行,每行包括两个整数X,Y,表示星球x和星球y之间有“以太”隧道,可以直接通讯。接下来的一行为一个整数k,表示将遭受攻击的星球的数目。接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这k个数互不相同,且都在0到n−1的范围内。


Output

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


Hint

1<=N<=2*M,1<=M<=200000,0<=X<Y。


Solution

因为正着不好想就倒着想,连通块个数初始就是n-k,然后当他们不是一个父亲且没broke就dounion,然后后面询问的时候再建立。最后记录答案,cnt不用初始化,因为要在broke的基础上建立。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 400005
using namespace std;
struct Edge{
	int u;
	int v;
	int time;
	int next;
}edge[maxn];
int first[maxn],last[maxn],FA[maxn],ans[maxn],orderr[maxn];
int node,n,m,x,y,k,cnt;
bool bro[maxn];
void addedge(int u,int v){
	edge[++node]=(Edge){u,v,0,0};
	if(first[u]==0)first[u]=node;
	else edge[last[u]].next=node;
	last[u]=node;
}
void init(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		addedge(x,y);
		addedge(y,x);
	}
	for(int i=1;i<=n;i++){
		FA[i]=i;
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++){
		scanf("%d",&orderr[i]);
		bro[orderr[i]]=true;
	}
	cnt=n-k;
}
int dofind(int x){
	if(FA[x]==x)return FA[x];
	else return FA[x]=dofind(FA[x]);
}
int dounion(int x,int y){
	int dx=dofind(x),dy=dofind(y);
	if(dx!=dy){
		FA[dx]=FA[dy];
	}
}
bool dofinD(int x,int y){
	return dofind(x)==dofind(y);
}
void workk(){
	for(int i=1;i<=2*m;i++){
		int a=edge[i].u,b=edge[i].v;
		if(!bro[a]&&!bro[b]&&!dofinD(a,b)){
			cnt--;
			dounion(a,b);
		}
	}
	ans[k+1]=cnt;
	for(int i=k;i>=1;i--){
		cnt++;
		bro[orderr[i]]=false;
		for(int p=first[orderr[i]];p;p=edge[p].next){
			int v=edge[p].v;
			if(!bro[v]&&!dofinD(orderr[i],v)){
				cnt--;
				dounion(orderr[i],v);
			}
		}
		ans[i]=cnt;
	}
}
int main(){
	init();
	workk();
	for(int i=1;i<=k+1;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2018-11-30 17:26  虚拟北方virtual_north。  阅读(129)  评论(0编辑  收藏  举报