【BZOJ】1015: [JSOI2008]星球大战starwar

1015: [JSOI2008]星球大战starwar

题意:一个点数为N(1<= 40w),边数为M(1<=20w)的图,总共删除k个节点,问开始以及每次删除一个节点之后图的连通块数?

思路:逆序并查集 即每次往图中加点;主要是因为逆序时,并查集的关系不会改变,最终在同一个连通块中的节点,之前一定还是在一个并查集中;

需注意加点时,如果该点还是孤立节点,这时就需要++sum;当然如果开始就增加连通分块的个数,之后加点的时候减去那也是一样的;

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 400004;
const int M = 200002;
int f[N],vis[N],del[N],ans[N];
int tot,head[N],sum;
void init()
{
    tot = 0;memset(head,0,sizeof(head));
}
struct Edge{
    int to,Next;
}e[M<<1];
inline void ins(int u,int v)
{
    e[++tot].Next = head[u]; e[tot].to = v; head[u] = tot;
}
int Find(int a)//**
{
    return a == f[a]?f[a]:f[a] = Find(f[a]);
}
void dfs(int u)
{
    vis[u] = 1;
    for(int id = head[u];id;id = e[id].Next){
        int v = e[id].to;
        if(vis[v]) continue;
        f[v] = Find(f[u]);
        dfs(v);
    }
}
int add(int u)
{
    for(int id = head[u];id;id = e[id].Next){
        int v = e[id].to;
        if(vis[v] == 2) continue;
        int fv = Find(v);
        if(f[u] == u){f[u] = fv;continue;}
        if(f[u] != u && f[u] != fv) sum--;
        f[fv] = f[u];
    }
    if(f[u] == u) sum++;//孤立节点
}
int main()
{
    int n,m,k,u,v;
    scanf("%d%d",&n,&m);
    init();
    for(int i = 0;i < m;i++){
        scanf("%d%d",&u,&v);
        ins(u,v);ins(v,u);
    }
    scanf("%d",&k);
    for(int i = 0;i < k;i++){
        scanf("%d",del + i);
        vis[del[i]] = 2;//2表示删除的节点,1表示在图中的节点
    }
    sum = 0;
    for(int i = 0;i < n;i++) f[i] = i;
    for(int i = 0;i < n;i++)if(!vis[i]) dfs(i),sum++;
    ans[k] = sum;
    for(int i = k - 1;i >= 0;i--){
        add(del[i]),vis[del[i]] = 1, ans[i] = sum;
    }
    for(int i = 0;i <= k;i++) printf("%d\n",ans[i]);
}

 

posted @ 2016-03-05 00:40  hxer  阅读(383)  评论(0编辑  收藏  举报