BZOJ 1015 - 变删点为加点 + 并查集维护

题意:给出一张无向图,每次删去其中一个点,每删一次就输出当前连通块的数量。
首先要明确一点:删去一个点,同时也删去了和这个点有关联的边集。但无论如何,删点并不好搞,所以我们可以考虑倒着来,加点,用并查集维护。具体来说,每次加上一个点x,如果一个点是被第一次删去的(一个点可能被删去多次)(然而数据中并没有这种情况),那么就将连通块数加1;否则不考虑。然后依次添上它的每一条相邻边,并查集维护之即可。
woc… 无向边M要乘2… 又忘了…

// BZOJ 1015

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

 const int M=200000*2+5, N=M*2;

 #define rep(i,a,b) for (int i=a; i<=b; i++)
 #define dep(i,a,b) for (int i=a; i>=b; i--)
 #define read(x) scanf("%d", &x)
 #define fill(a,x) memset(a, x, sizeof(a))

 int fa[N], cnt, n, m, ans[N], u, v, k, t, del[N];
 bool exi[N];

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

  struct Graph {
    int s, from[M], to[M], pre[M], last[N];
    void init() { s=0; rep(i,0,n-1) last[i]=0; }
    void ine(int a, int b) {
        s++;
        from[s]=a, to[s]=b, pre[s]=last[a];
        last[a]=s;
    }
    void ine2(int a, int b) {
        ine(a, b);
        ine(b, a);
    }
 } G;
 #define reg(i,G,u) for (int i=G.last[u]; i; i=G.pre[i])

 void add(int x, int t) {
    cnt++;
    reg(i,G,x) {
        int y=G.to[i];
        if (!exi[y]) continue;
        int fx=find(x), fy=find(y);
        if (fx!=fy) cnt--;
        fa[fx]=fy;
    }
    exi[x]=true;
    ans[t]=cnt;
 }

int main()
{
    read(n); read(m);
    G.init();
    rep(i,0,n-1) fa[i]=i;
    rep(i,1,m) read(u), read(v), G.ine2(u, v);
    read(k);
    fill(exi, true);
    rep(i,1,k) read(del[i]), exi[del[i]]=false;
    cnt=0;
    rep(x,0,n-1) if (exi[x]) add(x, n+1);
    ans[k]=cnt;

    dep(i,k,1) {
        int x=del[i]; 
        add(x, i-1);
    }
    rep(i,0,k) printf("%d\n", ans[i]);

    return 0;
}
posted @ 2016-01-07 18:45  Armeria  阅读(207)  评论(0编辑  收藏  举报