[luogu1197] [JSOI2008]星球大战

传送门

看到联通块,好像跟并查集、强连通分量有关系吧,仔细一看跟哪些点属于哪些块没关系,只关心联通块数量,那么应该可以用并查集做。继续看,这是一道删边的题,好像很难维护删边,我们又知道并查集是可以维护加边的,那么我们就倒过来做好了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 400005

struct Node{
    int u,v;
}E[MAXN];
struct edge{
    int v,next;
}G[MAXN];
int fa[MAXN],vis[MAXN];
int ed[MAXN],ans[MAXN];
int head[MAXN];
int N,M,K,tot=0;

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

inline void merge(int u,int v){
    fa[find(u)] = find(v);
}

inline void add(int u,int v){
    G[++tot].v = v ;G[tot].next = head[u];head[u] = tot;
}

int main(){

    scanf("%d%d",&N,&M);
    for(register int i=0;i<N;++i){
        fa[i] = i;
    }
    std::memset(head,0,sizeof(head));
    std::memset(vis,0,sizeof(vis));

    for(register int i=1;i<=M;++i){
        scanf("%d%d",&E[i].u,&E[i].v);
        add(E[i].u,E[i].v);
        add(E[i].v,E[i].u);
    }

    scanf("%d",&K);
    int count = N-K;

    for(register int i=K;i>=1;--i){
        scanf("%d",&ed[i]);
        vis[ed[i]] = 1;
    }

    for(register int i=1;i<=M;++i){
        int u = E[i].u;int v = E[i].v;
        if(vis[u]||vis[v]||(find(u)==find(v)))continue;
        merge(u,v);
        count--;
    }

    ans[0] = count;
    for(register int i=1;i<=K;++i){

        vis[ed[i]] = 0;
        count++;
        for(register int j=head[ed[i]];j;j=G[j].next){
            int v = G[j].v;
            if(!vis[v]&&find(v)!=find(ed[i])){
                merge(v,ed[i]);
                count--;
            }
        }
        ans[i] = count;
    }

    for(register int i=K;i>=0;--i){
        printf("%d\n",ans[i]);
    }

    return 0;
}
posted @ 2018-10-19 00:21  Neworld1111  阅读(129)  评论(0编辑  收藏  举报