BZOJ1015 [JSOI2008]星球大战starwar
Description
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的
机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直
接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划
地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首
领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每
一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则
这两个星球在同一个连通块中)。
Input
输入文件第一行包含两个整数,N (1 < = N < = 2M) 和M (1 < = M < = 200,000),分别表示星球的
数目和以太隧道的数目。星球用 0 ~ N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0 < = X <>
Y 表示星球x和星球y之间有“以太”隧道,可以直接通讯。接下来的一行为一个整数k,表示将遭受攻击的星球的
数目。接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这k个数互不相同,且都在0到n-1的范
围内。
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
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
1
1
2
3
3
正解:离线+并查集
解题报告:
正着做感觉很不可做,数据太大了,那么考虑尽可能少做删点操作。不妨离线操作,变删点为倒着加点,每次插一个点进入,并查集维护。
注意第一次做的时候,需要把直到最后都没有被摧回的点当做新点不断加入,并维护并查集。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 4000011; 21 int n,m,k,ecnt,cnt; 22 int first[MAXN],to[MAXN],next[MAXN]; 23 int stop[MAXN],father[MAXN]; 24 bool pd[MAXN],vis[MAXN]; 25 int ans[MAXN]; 26 27 inline int getint() 28 { 29 int w=0,q=0; 30 char c=getchar(); 31 while((c<'0' || c>'9') && c!='-') c=getchar(); 32 if (c=='-') q=1, c=getchar(); 33 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 34 return q ? -w : w; 35 } 36 37 inline int find(int x){ 38 if(father[x]!=x) father[x]=find(father[x]); 39 return father[x]; 40 } 41 42 inline void update(int x){ 43 int r1,r2=find(x); 44 for(int i=first[x];i;i=next[i]) { 45 int v=to[i]; if(!vis[v]) continue; 46 r1=find(v); if(r1!=r2) { cnt--; father[r1]=r2; } 47 } 48 } 49 50 inline void work(){ 51 n=getint(); m=getint(); 52 int x,y; 53 for(int i=1;i<=m;i++) { 54 x=getint()+1; y=getint()+1; 55 next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; 56 next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; 57 } 58 k=getint();for(int i=1;i<=k;i++) stop[i]=getint()+1,pd[stop[i]]=1; 59 for(int i=1;i<=n;i++) father[i]=i; 60 for(int i=1;i<=n;i++) if(!pd[i]) { cnt++; update(i); vis[i]=1;} 61 ans[k+1]=cnt; 62 for(int i=k;i;i--) {//注意顺序 63 cnt++;//事先加一个,如果处在本来的连通块中则最后减去即可 64 update(stop[i]); 65 vis[stop[i]]=1; 66 ans[i]=cnt; 67 } 68 for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]); 69 } 70 71 int main() 72 { 73 work(); 74 return 0; 75 }
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!