BZOJ1015[JSOI2008]星球大战starwar题解报告

题目链接

考虑正序去除点去掉其所有连边十分复杂,可以倒序离线处理,每次新建一个点,连接其连边,用并查集统计联通块的个数。
附代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN=400000+5;
int ans[MAXN],fa[MAXN],rnk[MAXN],hd[MAXN],des[MAXN];
int ff[MAXN],tt[MAXN];
int to[MAXN<<1],nxt[MAXN<<1];
bool used[MAXN];
int cnt,n,m;

inline void build(int f,int t)
{
    to[++cnt]=t;
    nxt[cnt]=hd[f];
    hd[f]=cnt;
}

int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

bool unite(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y) return 0;
    if(rnk[x]==rnk[y])
        fa[x]=y,++rnk[y];
    else if(rnk[x]>rnk[y])
        fa[y]=x;
    else
        fa[x]=y;
    return 1;
}

int main()
{
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;++i)
        fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&ff[i],&tt[i]);
        build(ff[i],tt[i]);
        build(tt[i],ff[i]);
    }
    int k;
    scanf("%d",&k);
    for(int i=1;i<=k;++i)
        scanf("%d",&des[i]),
        used[des[i]]=1;
    int ansn=n-k;
    for(int i=1;i<=m;++i)
    {
        if(!used[ff[i]]&&!used[tt[i]])
        {
            if(unite(ff[i],tt[i]))
                --ansn;
        }
    }
    for(int j=k;j>=1;--j)
    {
        ans[j]=ansn;
        ++ansn;
        int u=des[j];
        used[u]=0;
        for(int i=hd[u];i;i=nxt[i])
        {
            int v=to[i];
            if(used[v]) continue;
            if(unite(u,v))
                --ansn;
        }
    }
    printf("%d
",ansn);
    for(int i=1;i<=k;++i)
        printf("%d
",ans[i]);
    return 0;
}