Description
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。
Input
输入文件第一行包含两个整数,N (1 <= N <= 2M) 和M (1 <= M <= 200,000),分别表示星球的数目和以太隧道的数目。星球用0~N-1的整数编号。接下来的M行,每行包括两个整数X, Y,其中(0<=X<>Y)//注:此处貌似漏了点东西……还有输入一个D表示顺序捣毁一共D个星球,对于每个D都要统计出此时的联通块个数
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
各种RE之后终于过了……泪流满面
离线乱搞
把所有数据读进来之后倒着加点,相当于倒着计算出某时刻的联通块个数。这样做的好处是每次只要插边然后并查集维护联通块个数即可。要注意每次加入一个点都要把它标记为可拓展(因为原来没加入之前图中不能拓展这个点),还有联通块个数+1。平摊复杂度O(m)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x;} int n,m,x,y,cnt,len,piece; struct ed{int to,next;}e[500001]; int head[500001],q[400001],fa[400001],ans[400001]; bool mark[400001]; inline int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);} inline void insert(int u,int v) { cnt++;e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt; cnt++;e[cnt].to=u;e[cnt].next=head[v];head[v]=cnt; } inline void add(int x) { int i=head[x],p=getfa(x),q; while(i) { if(!mark[e[i].to]) { q=getfa(e[i].to); if(p!=q){fa[q]=p;piece--;} } i=e[i].next; } } int main() { n=read(); m=read(); for (int i=1;i<n;i++) fa[i]=i; for (int i=1;i<=m;i++) { x=read(); y=read(); insert(x,y); } len=read(); for (int i=1;i<=len;i++) { q[i]=read(); mark[q[i]]=1; } for (int i=0;i<n;i++) if (!mark[i]) { piece++; add(i); } ans[len+1]=piece; for (int i=len;i>=1;i--) { piece++; add(q[i]); mark[q[i]]=0; ans[i]=piece; } for (int i=1;i<=len+1;i++)printf("%d\n",ans[i]); return 0; }
——by zhber,转载请注明来源