bzoj 1015 [JSOI2008]星球大战starwar
并查集
题目中是说将节点一个个摧毁,正着做并不好做
所以考虑在所有的星球摧毁后反着进行连边
在恢复一个节点后,则恢复了这个节点到之前已恢复的节点的连边,然后用并查集维护联通块即可
注意在同一个联通块中连边是不会改变联通块的个数的
所以只要处理联通块之间的边即可
#include <bits/stdc++.h> using namespace std; const int MAXN=4*1e5+100; int n,m,k,fa[MAXN],q[MAXN],color[MAXN]; int now,nf[MAXN],vi[MAXN]; vector <int> e[MAXN],ans; int find(int x)//并查集 { if (fa[x]==x) return fa[x]; fa[x]=find(fa[x]); return fa[x]; } void dfs(int x,int now) { color[x]=now; for (int i=0;i<(int)e[x].size();i++) { if (color[e[x][i]]) continue; dfs(e[x][i],now); } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } scanf("%d",&k); for (int i=1;i<=k;i++) { scanf("%d",&q[i]); vi[q[i]]=1; } int co=0; for (int i=0;i<n;i++) { if (color[i]) continue; co++; dfs(i,co); }//对残余的图进行染色,判断联通块 printf("%d\n",co); for (int i=0;i<n;i++) fa[i]=i; for (int i=0;i<n;i++) { if (vi[i]) continue; for (int j=0;j<(int)e[i].size();j++) { if (vi[e[i][j]]) continue; if (find(e[i][j])!=find(i)) { fa[find(i)]=find(e[i][j]); } } } for (int i=0;i<n;i++) { if (vi[i]) continue; if (nf[find(i)]==0) { now++; nf[find(i)]=1; } } for (int i=k;i>=1;i--) { ans.push_back(now); int u; u=q[i]; vi[u]=0; for (int j=0;j<(int)e[u].size();j++) { if (vi[e[u][j]]) continue; int v; v=e[u][j];//进行加边 if (find(u)!=find(v)) { if (nf[find(u)]==1 && nf[find(v)]==1) { nf[find(u)]=0; now--; } fa[find(u)]=find(v); } } if (nf[find(u)]==0) { nf[find(u)]=1; now++; } } for (int i=(int)ans.size()-1;i>=0;i--) printf("%d\n",ans[i]); }