这道题和以前做过的一道经典的洪水冲桥问题很像,主要做法是逆向思维。(BZOJ第10道非SB题纪念)
先给出题目
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 #include<stdio.h> 2 struct shit{ 3 int aim,next; 4 }e[401000]; 5 int head[401000],num,s[401000],ans[401000],rank[401000],fa[401000],point; 6 bool mp[401000],us[401000]; 7 int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 8 void fuck(int x,int y) 9 { 10 e[++point].aim=y,e[point].next=head[x],head[x]=point; 11 } 12 void insert(int x) 13 { 14 int i=head[x],p=find(x),q; 15 while(i) 16 { 17 if(us[e[i].aim]) 18 { 19 q=find(e[i].aim); 20 if(q!=p)fa[q]=p,num--; 21 } 22 i=e[i].next; 23 } 24 } 25 int main() 26 { 27 int d,n,m,a,b; 28 scanf("%d%d",&n,&m); 29 for(int i=1;i<=n;++i)fa[i]=i; 30 for(int i=1;i<=m;++i) 31 { 32 scanf("%d%d",&a,&b); 33 fuck(a,b); 34 fuck(b,a); 35 } 36 scanf("%d",&d); 37 for(int i=1;i<=d;++i) 38 { 39 scanf("%d",&s[i]); 40 mp[s[i]]=true; 41 } 42 for(int i=0;i<n;++i) 43 if(!mp[i]) 44 { 45 num++; 46 insert(i); 47 us[i]=true; 48 } 49 ans[d+1]=num; 50 for(int i=d;i;i--) 51 { 52 num++; 53 insert(s[i]); 54 us[s[i]]=true; 55 ans[i]=num; 56 } 57 for(int i=1;i<=d+1;++i)printf("%d ",ans[i]); 58 return 0; 59 }