并查集+时光倒流 || [JSOI2008]星球大战starwar || BZOJ 1015 || Luogu P1197
题解:
坑点有点多啊,加上我本来就有点头昏脑涨,一道水题写了一万年。。
并查集不支持拆开(但是可以撤销合并),只支持合并。所以把询问离线了,从最后状态到初状态开始一个个往当前图里加点。
CZL:对于只有删除点/边而不增加点/边,且允许离线的题,可以考虑时光倒流,先建出最终情况,再倒着把点/边加回去。
代码:
1 #include<cstdio> 2 using namespace std; 3 inline int rd(){ 4 int x=0; char c=getchar(); 5 while(c<'0'||c>'9')c=getchar(); 6 while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} 7 return x; 8 } 9 const int maxn=4e5+5,maxm=2e5+5; 10 int N,M,fa[maxn],QUE[maxn],num_edge=0,edge_head[maxn]; 11 int u,v,K,cnt=0,f1,f2,ans[maxn]; 12 bool atkd[maxn]; 13 struct Edge{ int to,nx; }edge[maxm<<1]; 14 inline void Add_edge(int from,int to){ 15 edge[++num_edge].nx=edge_head[from]; 16 edge[num_edge].to=to; 17 edge_head[from]=num_edge; 18 return; 19 } 20 inline int getf(int n){ 21 if(fa[n]==n) return n; 22 fa[n]=getf(fa[n]); 23 return fa[n]; 24 } 25 int main(){ 26 N=rd(); M=rd(); 27 for(int i=0;i<N;i++) fa[i]=i; 28 for(int i=1;i<=M;i++){ 29 u=rd(); v=rd(); 30 Add_edge(u,v); 31 Add_edge(v,u); 32 f1=getf(u); f2=getf(v); 33 if(f1!=f2) fa[f1]=f2; 34 } 35 for(int i=0;i<N;i++){ 36 if(fa[i]==i) ans[0]++; 37 fa[i]=i; 38 } 39 K=rd(); 40 for(int i=1;i<=K;i++){ 41 QUE[i]=rd(); 42 atkd[QUE[i]]=1; 43 } 44 for(int i=0;i<N;i++) 45 if(atkd[i]==0){ 46 for(int j=edge_head[i];j;j=edge[j].nx){ 47 int y=edge[j].to; 48 if(atkd[y]) continue; 49 f1=getf(i); f2=getf(y); 50 if(f1!=f2) fa[f1]=f2; 51 } 52 } 53 for(int i=0;i<N;i++) 54 if(atkd[i]==0&&fa[i]==i) cnt++; 55 for(int k=K;k>=1;k--){ 56 ans[k]=cnt; 57 cnt++; 58 int x=QUE[k]; 59 atkd[x]=0; 60 for(int i=edge_head[x];i;i=edge[i].nx){ 61 int y=edge[i].to; 62 if(atkd[y]) continue; 63 f1=getf(x); f2=getf(y); 64 if(f1!=f2){ 65 cnt--; 66 fa[f1]=f2; 67 } 68 } 69 } 70 for(int i=0;i<=K;i++) printf("%d\n",ans[i]); 71 return 0; 72 }
By:AlenaNuna