若是城市A和城市B互通,城市B和城市C互通,那么城市A和城市C也互通,A、B、C三个城市算一个汇集点。先已知有n个城市和m条道路,想求的是有几个汇集点?但小S以为太简单了,因为战争缘由,某些城市会被导弹销毁掉,与之相应的道路也变得不可用。以前已经被销毁的不会被复原。现给定每次销毁的城市顺序,求每次销毁后汇集点有多少个。node
第一行输入n和m,表示城市数量和道路数量(1≤n≤104,1≤m≤2n)(1≤n≤104,1≤m≤2n)ios
接下来m行,每行输入两个数ai和bi(1≤ai,bi≤n)(1≤ai,bi≤n)。表示ai和bi直接有道路c++
第m+2行输入q,表示有q个城市会被销毁 (1≤q≤n)(1≤q≤n)spa
接下来输入q个数,每行输入一个不重复的数,表示被销毁的城市code
输出一行q个数,每i个数表示第i个城市销毁后汇集点的数量ci
8 9 1 2 1 3 1 6 2 4 3 6 4 5 4 7 5 7 5 8 4 3 2 5 4
1 2 3 3
并查集,经过储存节点反向建树,以后再一个个加点寻找集合个数it
#include <bits/stdc++.h> using namespace std; const int maxn=1e4+10; struct node{ int a,b; bool flag;//已经加过的点无须再加,经过flag进行标记 }node[maxn<<1]; int pre[maxn<<1]; int n,m,t; vector<int> res;//储存须要删除的节点 stack<int> st;//储存集合个数 bool vis[maxn<<1]; int find(int x) { return pre[x]=(pre[x]==x?x:find(pre[x]));//状态压缩 } void unite(int x,int y) { x=find(x),y=find(y); if(x>y) pre[x]=y;//秩低向秩高的合并,防止树的退化 else pre[y]=x; } int main() { ios::sync_with_stdio(false); memset(vis,0,sizeof(vis)); cin>>n>>m; for(int i=1;i<=n;i++) pre[i]=i; for(int i=0;i<m;i++) { cin>>node[i].a>>node[i].b; node[i].flag=false; } cin>>t; for(int i=0;i<t;i++) { int tmp; cin>>tmp; vis[tmp]=true;//标记已经被删除的点 res.push_back(tmp); } vector<int>::iterator it; /*it=res.end()-1; for(int i=0;i<m;i++) { if(!vis[node[i].a]&&!vis[node[i].b]&&!node[i].flag) { unite(node[i].a,node[i].b); node[i].flag=true; cout<<i<<endl; } }*/ while(t--) { it=res.end()-1; for(int i=0;i<m;i++) { if(!vis[node[i].a]&&!vis[node[i].b]&&!node[i].flag) { unite(node[i].a,node[i].b); node[i].flag=true; } } int sum=0; for(int i=1;i<=n;i++) if(find(i)==i) sum++; st.push(sum); vis[*it]=false;//加点 res.erase(it); } int cnt=1; while(!st.empty()) { cout<<st.top()-cnt++<<' '; st.pop(); } cout<<endl; return 0; }
应有更快的解法io