【题意分析】
给你一张无向图,要求支持删点和询问连通块数。
【解题思路】
可以直接可持久化并查集大力艹过去。
考虑到正着删点就是倒着加点,所以并不需要可持久化。复杂度O((k+m)α(n))。
【参考代码】
当时还在玩泥巴,实现有点naive,常数奇大无比。。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<set> 4 #define REP(I,start,end) for(int I=start;I<=end;I++) 5 #define PER(I,start,end) for(int I=start;I>=end;I--) 6 using namespace std; 7 set<int> tree; 8 int n,m,k,edge=0,q[400001],father[400001],head[400001],next[400001],point[400001],sav[400001]; 9 bool destroyed[400001]; 10 inline void addEdge(int from,int to) 11 { 12 next[++edge]=head[from]; 13 head[from]=edge; 14 point[edge]=to; 15 } 16 inline int getFa(int n) 17 { 18 if(father[n]==n) 19 return n; 20 return father[n]=getFa(father[n]); 21 } 22 int main() 23 { 24 scanf("%d%d",&n,&m); 25 REP(i,1,m) 26 { 27 int u,v; 28 scanf("%d%d",&u,&v); 29 addEdge(u,v); 30 addEdge(v,u); 31 } 32 scanf("%d",&k); 33 memset(destroyed,0,sizeof(destroyed)); 34 REP(i,1,k) 35 { 36 scanf("%d",q+i); 37 destroyed[q[i]]=true; 38 } 39 REP(i,0,n-1) 40 father[i]=i; 41 REP(i,0,n-1) 42 if(!destroyed[i]) 43 { 44 int t=head[i]; 45 while(t) 46 { 47 int p=point[t]; 48 if(!destroyed[p]) 49 father[getFa(p)]=getFa(i); 50 t=next[t]; 51 } 52 } 53 tree.clear(); 54 REP(i,0,n-1) 55 { 56 int fa=getFa(i); 57 if(!destroyed[i]&&tree.find(fa)==tree.end()) 58 tree.insert(fa); 59 } 60 sav[k]=tree.size(); 61 PER(i,k-1,0) 62 { 63 int P=q[i+1],t=head[P]; 64 destroyed[P]=false; 65 tree.clear(); 66 while(t) 67 { 68 int p=point[t]; 69 if(!destroyed[p]) 70 { 71 int fa2=getFa(p); 72 tree.insert(fa2); 73 father[getFa(P)]=fa2; 74 } 75 t=next[t]; 76 } 77 sav[i]=sav[i+1]-tree.size()+1; 78 } 79 REP(i,0,k) 80 printf("%d\n",sav[i]); 81 return 0; 82 }