洛谷P1197 [JSOI2008]星球大战

洛谷P1197 [JSOI2008]星球大战

题目描述

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

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

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

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

输入输出格式

输入格式:

输入文件第一行包含两个整数,\(N\)(\(1 \leq N \leq 2M\))和\(M\)(\(1\) $ \leq $ \(M\) $ \leq $ 200,000),分别表示星球的数目和以太隧道的数目。星球用 0~\(N−1\) 的整数编号。

接下来的 \(M\) 行,每行包括两个整数 \(X\), \(Y\),其中 ( 0$ \leq X<>Y$ ) 表示星球 \(x\) 和星球 \(y\) 之间有 “以太” 隧道,可以直接通讯。

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

接下来的 \(k\) 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 \(k\) 个数互不相同,且都在 0n−1 的范围内。

输出格式:

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

思路

并查集
从反向思考
先考虑所有会被摧毁的点都被摧毁后的情况,这个时候利用并查集可以轻松求出有几个联通块
然后从后往前算
每加入一个点就把这个点加入图中(增加一个联通块),并枚举每一个和他连着的边,对这个边的两个顶点进行合并操作,如果合并操作前一条边连着的两个点在两个集合,那么联通块数量就减一,否则不做处理。
最后正向输出结果就行了

上代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 400010
struct Node{
	int u,v;
	Node(){}
	Node(int u,int v):u(u),v(v){}
}p[MAXN];
int f[MAXN],head[MAXN],Next[MAXN],distroy[MAXN],ans[MAXN];
bool vis[MAXN];
int i,j,k,m,n,u,v,tot;
char readc;
void read(int &n){
	while((readc=getchar())<48||readc>57);
	n=readc-48;
	while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
void addNode(int u,int v){
	p[++tot]=Node(u,v);
	Next[tot]=head[u],head[u]=tot;
	p[++tot]=Node(v,u);
	Next[tot]=head[v],head[v]=tot;
}
int find(int n){
	return f[n]==n?f[n]:f[n]=find(f[n]);
}
bool _union(int x,int y){
	int a=find(x),b=find(y);
	if(a==b) return false;
	f[b]=a;
	return true;
}
int main(){
	read(n);
	read(m);
	for(i=1;i<=n;i++) f[i]=i,vis[i]=true;
	memset(head,-1,sizeof(head));
	tot=-1;
	for(i=1;i<=m;i++){
		read(u),read(v);
		u++,v++;
		addNode(u,v);
	}
	read(k);
	for(i=1;i<=k;i++) read(distroy[i]),distroy[i]++,vis[distroy[i]]=false;
	ans[k+1]=n-k;
	for(i=0;i<=tot;i++)
		if(vis[p[i].u]&&vis[p[i].v]) {
			if(_union(p[i].u,p[i].v)){
				ans[k+1]--;
			}
		}
	for(i=k;i>=1;i--){
		int tmp=distroy[i];
		vis[tmp]=true;
		ans[i]=ans[i+1]+1;
		for(j=head[tmp];j+1;j=Next[j]){
			if(vis[p[j].u]&&vis[p[j].v]&&_union(p[j].u,p[j].v)){
				ans[i]--;
			}
		}
	}
	for(i=1;i<=k+1;i++) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2018-11-30 21:15  EternalBlue  阅读(161)  评论(0)    收藏  举报