我存图用的邻接表,总觉得应该有更好的方法,无奈智商不够想不出来。
闲话少说,说一说我的步骤:(整体思路,离线、并查集)
1.存图(邻接表),初始cnt为目前的连通块个数 n-k(当问题以外的点之间没有边,即他们每个点都是一个连通块),用que数组存第i个问题的点,用vis数组记录此点是第几个问题;
2.先把问题里没有出现的点之间的边加进图中,更新cnt;
3.从最后一个问题开始倒着循环,ans[i]表示未加入此时删除的点的连通块个数。把每个点关联的边加入图中,并判断如果这条边另一端的点j没有被删除(即vis[j]==0 || vis[i]<vis[j]),如果为真,更新cnt(cnt--),但由于连通快减少的个数总比满足条件的加入的边的个数少1,所以每次cnt还要加1;
4.激动人心的时刻就要到了:输出ans数组,AC!
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#define MAXN 200010
using namespace std;
struct edge{
int to;
edge * p;
} *ed[2*MAXN];
int que[MAXN*2];
int ans[MAXN*2];
int f[MAXN*2];
int vis[MAXN*2];
void add(int a,int b){
edge * point=new edge;
point->to=b;
point->p=ed[a];
ed[a]=point;
}
int getf(int x){
return f[x]==x ? x : f[x]=getf(f[x]);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
int k;
memset(vis,0,sizeof(vis));
scanf("%d",&k);
for(int i=1;i<=k;i++){
scanf("%d",&que[i]);
vis[que[i]]=i;
}
int cnt=n-k;
for(int i=0;i<n;i++) f[i]=i;
for(int i=0;i<n;i++)
if(!vis[i])
for(edge* j=ed[i];j;j=j->p){
if(!vis[j->to] && getf(i)!=getf(j->to)){
f[getf(j->to)]=getf(i);
cnt--;
}
}
for(int i=k;i>=1;i--){
ans[i]=cnt;
for(edge* j=ed[que[i]];j;j=j->p){
int now=j->to;
if(getf(now)!=getf(que[i]) && (!vis[now] || vis[now]>vis[que[i]])){
f[getf(now)]=getf(que[i]);
cnt--;
}
}
cnt++;
}
ans[0]=cnt;
for(int i=0;i<=k;i++) printf("%d\n",ans[i]);
return 0;
}