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

来源:互联网 发布:淘宝羊绒哈伦九分女裤 编辑:程序博客网 时间:2024/05/11 12:30

题意:给出一张无向图,每次删去其中一个点,每删一次就输出当前连通块的数量。
首先要明确一点:删去一个点,同时也删去了和这个点有关联的边集。但无论如何,删点并不好搞,所以我们可以考虑倒着来,加点,用并查集维护。具体来说,每次加上一个点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;}
0 0
原创粉丝点击