8 13 0 1 1 6 6 5 5 0 0 6 1 2 2 3 3 4 4 5 7 1 7 2 7 6 3 6 5 1 6 3 5 7
1 1 1 2 3 3
并查集
Hungar
用最朴素的做法,我们要建很多次图,时间上不允许,所以得想点什么优化,本弱刚看到也没什么头绪,后来听说要把操作保留,然后逆序操作,瞬间就折服了
我们先构建一张图,其中不包括烧掉的点和边,那么这就是最后的情况了,之后我们每次都添加被烧的那个点和附属的边。原来是去点,现在相当于从最后加点。
#include<stdio.h> #include<string.h> const int maxn=400010; int father[maxn]; bool flag[maxn]; int turn[maxn]; int ans2[maxn]; struct node { int to; int next; }edge[maxn]; int head[maxn]; int tot,ans; void addedge(int from,int to) { edge[tot].to=to; edge[tot].next=head[from]; head[from]=tot++; } int find(int x) { if(father[x]==-1) return x; return father[x]=find(father[x]); } void Union(int x,int y) { int a=find(x); int b=find(y); if(a!=b) { father[a]=b; ans--; } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { memset(flag,0,sizeof(flag)); memset(head,-1,sizeof(head)); memset(father,-1,sizeof(father)); tot=0; int x,y; for(int i=0;i<m;i++) { scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } int t; scanf("%d\n",&t); ans=n-t; for(int i=0;i<t;i++) { scanf("%d",&turn[i]); flag[turn[i]]=1; } for(int i=0;i<n;i++) { if(flag[i]) continue; for(int j=head[i];j!=-1;j=edge[j].next) if(!flag[edge[j].to]) Union(i,edge[j].to); //最后的情况时2个点都没被烧掉的才可以并 } ans2[t]=ans; for(int i=t-1;i>=0;i--) { flag[turn[i]]=0; ans++; for(int j=head[turn[i]];j!=-1;j=edge[j].next) { if(!flag[edge[j].to]) Union(turn[i],edge[j].to); } ans2[i]=ans; } for(int i=0;i<=t;i++) printf("%d\n",ans2[i]); } return 0; }