题意:
给你一个无向图,每次从图中删去一个点,询问每次删点过后图中连通块的数量
思路:
本题可以离线,因此我们采用离线的逆向做法
怎么个逆向呢?我们假设一开始只有所有删点操作之后的点,并算出连通块个数
之后每次向图中加入被删除的点,并统计连通块个数
如果重新对所有点跑一遍的话时间复杂度上一定会炸,对于新加入的点,我们先对当前连通块个数加1,如果遍历该点连接的所有点,如果能够合并,那么就将连通块个数减1
最后把答案倒序输出就好了
#include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<stack> #include<cstring> using namespace std; const int maxn=4e5+1000; int flag[maxn],fa[maxn]; vector<int>a[maxn]; stack<int> s,q; int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));} int main() { int n,m,k,u,v; memset(flag,1,sizeof(flag)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); a[u].push_back(v); a[v].push_back(u); } scanf("%d",&k); for(int i=1;i<=k;i++){ scanf("%d",&u); s.push(u); flag[u]=0; } int cnt=n-k; for(int i=0;i<n;i++) fa[i]=i; for(int i=0;i<n;i++){ if(!flag[i]) continue; else{ for(int j=0;j<a[i].size();j++){ if(flag[a[i][j]]){ int f1=find(a[i][j]),f2=find(i); if(f1!=f2) fa[f1]=find(fa[f2]),cnt--; } } } } q.push(cnt); for(int i=1;i<=k;i++){ cnt++; int x=s.top(); s.pop(); for(int j=0;j<a[x].size();j++){ if(flag[a[x][j]]){ int f1=find(a[x][j]),f2=find(x); if(f1!=f2) fa[f1]=find(fa[f2]),cnt--; } } flag[x]=1; q.push(cnt); } while(!q.empty()){ cout<<q.top()<<endl; q.pop(); } return 0; }