[BZOJ1015][JSOI2008]星球大战starwar(并查集)

题目描述

传送门

题解

正着拆不好拆,那么反向加。
计数的时候简单判断。

代码

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

const int max_m=2e5+5;
const int max_n=max_m*2;
const int max_k=max_n;
const int max_e=max_m*2;

int n,m,x,y,k,alive;
int tot,point[max_n],next[max_e],v[max_e];
int f[max_n];
int query[max_k],ans[max_k];
bool distroyed[max_n];

inline void addedge(int x,int y){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}

inline int find(int x){
    if (x==f[x]) return x;
    f[x]=find(f[x]);
    return f[x];
}

inline void merge(int x,int y){
    int f1=find(x);
    int f2=find(y);
    f[f1]=f2;
}

int main(){
// freopen("input.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;++i){
        scanf("%d%d",&x,&y);
        ++x; ++y;
        addedge(x,y);
    }
    scanf("%d",&k);
    for (int i=1;i<=k;++i){
        scanf("%d",&query[i]);
        ++query[i];
        distroyed[query[i]]=true;
    }

    alive=n-k;
    for (int i=1;i<=n;++i) f[i]=i;

    for (int i=1;i<=n;++i)
      if (!distroyed[i]){
        for (int j=point[i];j;j=next[j])
          if (!distroyed[v[j]]&&find(v[j])!=find(i)){
            merge(i,v[j]);
            --alive;
          }
      }
    ans[k]=alive;

    for (int i=k;i>=1;--i){
        distroyed[query[i]]=false;
        alive++;
        for (int j=point[query[i]];j;j=next[j])
          if (!distroyed[v[j]]&&find(v[j])!=find(query[i])){
            merge(query[i],v[j]);
            --alive;
          }
        ans[i-1]=alive;
    }

    for (int i=0;i<=k;++i)
      printf("%d\n",ans[i]);
}

总结

新的并查集技巧。

你可能感兴趣的:([BZOJ1015][JSOI2008]星球大战starwar(并查集))