[JSOI2008]星球大战
OJ题号:BZOJ1015、洛谷1197
思路:并查集。
将已经合并的结点拆开并不容易,因此可以考虑将此题倒着操作,把不连通的结点合并。
由题意得总共有n个结点,其中k个结点最终被删除,因此最终剩下的结点为n-k。
首先对这n-k个结点进行合并操作。然后将删除的点逆序加入图中,同时进行合并操作。每进行一次合法的合并操作就意味着减少一个联通块,同时要注意加入结点时,该结点本身也算一个新的联通块。
最后按原顺序输出每次的联通块个数。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<stack> 5 const int N=400000; 6 class UnionFindSet { 7 private: 8 int anc[N]; 9 int Find(int x) { 10 return (x==anc[x])?x:(anc[x]=Find(anc[x])); 11 } 12 public: 13 UnionFindSet(int n) { 14 for(int i=0;i<n;i++) anc[i]=i; 15 } 16 bool isConnected(int x,int y) { 17 return Find(x)==Find(y); 18 } 19 void Union(int x,int y) { 20 anc[Find(y)]=Find(x); 21 } 22 }; 23 int main() { 24 int n,m; 25 scanf("%d%d",&n,&m); 26 std::vector<int> edge[n]; 27 for(int i=0;i<m;i++) { 28 int x,y; 29 scanf("%d%d",&x,&y); 30 edge[x].push_back(y); 31 edge[y].push_back(x); 32 } 33 int k; 34 scanf("%d",&k); 35 std::stack<int> attack_list; 36 bool attacked[n]; 37 memset(attacked,0,sizeof attacked); 38 for(int i=0;i<k;i++) { 39 int x; 40 scanf("%d",&x); 41 attack_list.push(x); 42 attacked[x]=true; 43 } 44 int ans=0; 45 UnionFindSet set(n); 46 for(int i=0;i<n;i++) { 47 if(!attacked[i]) { 48 ans++; 49 for(std::vector<int>::iterator j=edge[i].begin();j<edge[i].end();j++) { 50 if(set.isConnected(i,*j)||attacked[*j]) continue; 51 set.Union(i,*j); 52 ans--; 53 } 54 } 55 } 56 std::stack<int> answers; 57 while(!attack_list.empty()) { 58 answers.push(ans); 59 ans++; 60 for(std::vector<int>::iterator i=edge[attack_list.top()].begin();i<edge[attack_list.top()].end();i++) { 61 if(set.isConnected(attack_list.top(),*i)||attacked[*i]) continue; 62 set.Union(attack_list.top(),*i); 63 ans--; 64 } 65 attacked[attack_list.top()]=false; 66 attack_list.pop(); 67 } 68 answers.push(ans); 69 while(!answers.empty()) { 70 printf("%d\n",answers.top()); 71 answers.pop(); 72 } 73 return 0; 74 }