【BZOJ】1015: [JSOI2008]星球大战starwar(并查集)

http://www.lydsy.com/JudgeOnline/problem.php?id=1015

看了题解的囧T_T,一开始以为是求割点,但是想到割点不能统计。。。。

这题用并查集,思想很巧妙。

我们按照逆序建图,也就是从最后一个毁了的星球之后建图。然后从后往前走。

那么怎么统计联通块呢?很简单,我们开个数组记录没有被破坏的星球,然后每一次都将一个星球拿去访问他连接的其他点,看他们是否在一个联通块,如果不是,那么就将连接的那个点加入自己的联通块。

#include <cstdio>

#include <cstring>

#include <cmath>

#include <string>

#include <iostream>

#include <algorithm>

using namespace std;

#define rep(i, n) for(int i=0; i<(n); ++i)

#define for1(i,a,n) for(int i=(a);i<=(n);++i)

#define for2(i,a,n) for(int i=(a);i<(n);++i)

#define for3(i,a,n) for(int i=(a);i>=(n);--i)

#define for4(i,a,n) for(int i=(a);i>(n);--i)

#define CC(i,a) memset(i,a,sizeof(i))

#define read(a) a=getint()

#define print(a) printf("%d", a)

#define dbg(x) cout << #x << " = " << x << endl

#define printarr(a, n, m) rep(aaa, n) { rep(bbb, m) cout << a[aaa][bbb]; cout << endl; }

inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }

inline const int max(const int &a, const int &b) { return a>b?a:b; }

inline const int min(const int &a, const int &b) { return a<b?a:b; }



const int N=400010;

int p[N], vis[N], ihead[N], cnt, tot, n, m, ans[N], q[N], des[N], d;

struct ED { int to, next; }e[N];

inline void add(const int &u, const int &v) {

	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v;

	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u;

}

const int ifind(const int &x) { return x==p[x]?x:p[x]=ifind(p[x]); }

inline void update(const int &u) {

	int fu=ifind(u), fv;

	for(int i=ihead[u]; i; i=e[i].next) if(vis[e[i].to]) {

		fv=ifind(e[i].to);

		if(fv!=fu) { p[fv]=fu; --tot; } //如果不在一个联通块但是却有边相连,那么就要合并

	}

}



int main() {

	read(n); read(m);

	int x, y;

	rep(i, n) p[i]=i;

	for1(i, 1, m) {

		read(x); read(y);

		add(x, y);

	}

	read(d);

	for1(i, 1, d) {

		read(q[i]);

		des[q[i]]=1;

	}

	rep(i, n) if(!des[i]) {

		++tot; //每一次都假设多了一个联通块

		update(i);

		vis[i]=1;

	}

	ans[d+1]=tot;

	for3(i, d, 1) {

		++tot;

		update(q[i]);

		vis[q[i]]=1;

		ans[i]=tot;

	}

	for1(i, 1, d+1) printf("%d\n", ans[i]);

	return 0;

}

 

 


 

 

 

Description

很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武 器统治者整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或 间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通 讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打 击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

Input

输 入文件第一行包含两个整数,N (1 <= N <= 2M) 和M (1 <= M <= 200,000),分别表示星球的数目和以太隧道的数目。星球用0~N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0<=X<>Y

Output

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

Sample Input

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

Sample Output

1
1
1
2
3
3

HINT

Source

你可能感兴趣的:(2008)