[BZOJ 1015][JSOI2008]星球大战starwar(并查集+离线)
Description
很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的
机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直
接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划
地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首
领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每
一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则
这两个星球在同一个连通块中)。
Solution
如果把删点的过程倒过来考虑的话,题就变得非常可做啦…
用并查集,先求出所有要删的点都删完时的连通块个数,然后再逆向把点一个一个加回去,记录答案
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define MAXN 400005 using namespace std; int n,m,k,cnt=0,tot,head[MAXN],father[MAXN],dot[MAXN],ans[MAXN]; bool del[MAXN]; struct Node{ int next,to; }Edges[MAXN]; int Read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } void addedge(int u,int v) { Edges[++cnt].next=head[u]; head[u]=cnt; Edges[cnt].to=v; } int Find(int x) { if(father[x]==x)return x; father[x]=Find(father[x]); return father[x]; } int main() { memset(head,-1,sizeof(head)); n=Read();m=Read(); for(int i=1;i<=m;i++) { int u,v; u=Read();v=Read(); addedge(u,v);addedge(v,u); } k=Read(); for(int i=1;i<=k;i++) { dot[i]=Read(); del[dot[i]]=1; } for(int i=0;i<n;i++)father[i]=i; tot=n-k; for(int i=0;i<n;i++) { int fu=Find(i); if(!del[i]) for(int j=head[i];~j;j=Edges[j].next) { int v=Edges[j].to; if(!del[v]) { int fv=Find(v); if(fv!=fu) father[fv]=fu,tot--; } } } ans[k]=tot; for(int i=k-1;i>=0;i--) { del[dot[i+1]]=0; tot++; int fu=Find(dot[i+1]); for(int j=head[dot[i+1]];~j;j=Edges[j].next) { int v=Edges[j].to; if(!del[v]) { int fv=Find(v); if(fu!=fv) father[fv]=fu,tot--; } } ans[i]=tot; } for(int i=0;i<=k;i++) printf("%d\n",ans[i]); return 0; }