BZOJ_1015_[JSOI2008]星球大战_并查集

BZOJ_1015_[JSOI2008]星球大战_并查集

题意:很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系。某一天,凭着一个偶然的
机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直
接或间接地连接。 但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划
地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。现在,反抗军首
领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每
一次打击之后反抗军占据的星球的连通快的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则
这两个星球在同一个连通块中)。

分析:离线,把删除当作插入,每次用并查集维护连通块个数。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 400050
int n,fa[N],m,q[N],ans[N],cnt;
int head[N],to[N<<1],nxt[N<<1],vis[N],k;
inline void add(int u,int v)
{
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;    
}
int find(int x)
{
    int p=fa[x];
    while(p!=fa[p])p=fa[p];
    while(x!=p){
        int tmp=fa[x];
        fa[x]=p;
        x=tmp;    
    }
    return fa[x];    
}
int main()
{
    register int i,j;
    int x,y;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)fa[i]=i;
    for(i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        x++,y++;
        add(x,y);add(y,x);
    }
    for(i=1;i<=n;i++)vis[i]=1;
    scanf("%d",&k);
    ans[k]=n-k;
    for(i=1;i<=k;i++){
        scanf("%d",&q[i]);
        q[i]++;
        vis[q[i]]=0;
    }
    for(i=1;i<=n;i++){
        if(!vis[i])continue;
        for(j=head[i];j;j=nxt[j]){
            if(!vis[to[j]])continue;
            int dj=find(to[j]),di=find(i);
            if(di!=dj){
                fa[di]=dj;ans[k]--;
            }
        }
    }
    vis[q[k]]=1;
    for(i=k-1;i>=0;i--){
        int x=q[i+1];
        ans[i]=ans[i+1]+1;
        for(j=head[x];j;j=nxt[j]){
            int t=to[j];
            if(!vis[t])continue;
            int dy=find(t),dx=find(x);    
            if(dx!=dy){
                fa[dy]=dx;ans[i]--;    
            }
        }
        vis[q[i]]=1;
    }
    for(i=0;i<=k;i++){
        printf("%d\n",ans[i]);
    }
}

 

posted @ 2018-02-21 17:15  fcwww  阅读(...)  评论(...编辑  收藏