<并查集>luogu 1197 星球大战

来源:互联网 发布:基于大数据用户画像 编辑:程序博客网 时间:2024/06/04 19:05

去题面的传送门
记得之前做过一个跟这个题很类似的。那道题是每次删一条边,问联通块的个数。正解就是离线处理,然后倒序加边,用并查集维护。而这道题是删点,同样想到离线处理然后倒序加点,唯一的不同是这道题要把这个点连接的所有边都for一遍。。。
话不多说,上代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=200000+100;int cnt,n,m,k;int fist[maxn<<1],nxt[maxn<<1],num[maxn<<1],ans[maxn<<1],fa[maxn<<1];bool vis[maxn<<1];struct hh{    int f,t;}e[maxn<<1];void build(int f,int t){    e[++cnt]=(hh){f,t};    nxt[cnt]=fist[f];    fist[f]=cnt;}int find(int x){    if(fa[x]==x) return x;    return fa[x]=find(fa[x]);}int main(){    memset(fist,-1,sizeof(fist));     scanf("%d%d",&n,&m);    for(int i=1;i<=m;++i)    {        int x,y;        scanf("%d%d",&x,&y);        build(x,y);        build(y,x);    }    scanf("%d",&k);    for(int i=1;i<=k;++i)    {        scanf("%d",&num[i]);        vis[num[i]]=true;    }    for(int i=1;i<=n;++i) fa[i]=i;    ans[k+1]=n-k;    for(int i=1;i<=cnt;i+=2)    {        int u=e[i].f,v=e[i].t;        int f1=find(u),f2=find(v);        if(!vis[u]&&!vis[v]&&f1!=f2)        {            f1=fa[f2];            ans[k+1]--;        }    }    for(int i=k;i>=1;--i)    {        ans[i]=ans[i+1]+1;        int u=num[i];        vis[u]=false;        for(int j=fist[u];j!=-1;j=nxt[j])        {            int v=e[j].t;            if(vis[v]) continue;            int f1=find(u),f2=find(v);            if(f1!=f2)            {                ans[i]--;                fa[f1]=f2;            }        }    }       for(int i=1;i<=k+1;++i) printf("%d\n",ans[i]);    return 0;}