bzoj 1015: [JSOI2008]星球大战starwar (逆向思维+并查集)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1015

思路: 题目是要我们对当前图拆掉k个点,问,每拆一个点后图中有多少个联通块,我们可以逆向思维,先处理出所有操作完成后的最终图,题目中破坏点的操作对于这个图来说就变成了加点扔到并查集的操作了,题目说的图一开始的联通块个数其实就是最终的图加上k个点后联通块的数量,第k个点被删后的图就是一开始处理得到的最终图,

 

实现代码:

#include<bits/stdc++.h>
using namespace std;
const int M = 4e5+10;
int f[M],b[M],c[M],vis[M];
vector<int>g[M];

struct node{
    int u,v; 
}a[M];

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

int mix(int x,int y){
    int fx = Find(x),fy = Find(y);
    if(fx != fy){
        f[fx] = fy;
        return 1;
    } 
    return 0;
}

int main()
{
    int n,m,k;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i ++){
        scanf("%d%d",&a[i].u,&a[i].v);
        g[a[i].u].push_back(a[i].v);
        g[a[i].v].push_back(a[i].u);
    }
    for(int i = 0;i <= n;i ++) f[i] = i;
    scanf("%d",&k);
    for(int i = 1;i <= k;i ++){
        cin>>b[i];
        vis[b[i]] = 1;
    }
    for(int i = 1;i <= m;i ++){
        if(vis[a[i].u]||vis[a[i].v]) continue;
        mix(a[i].u,a[i].v);
    }
    int ans = 0;
    for(int i = 0;i < n;i ++){
        if(f[i] == i&&!vis[i]) ans ++;
    }
    c[k+1] = ans;
    for(int i = k;i >= 1;i --){
        ans ++; vis[b[i]] = 0;
        for(int j = 0;j < g[b[i]].size();j ++){
            int v = g[b[i]][j];
            if(!vis[v])
            ans -= mix(b[i],v);
        }
        c[i] = ans;
    }
    for(int i = 1;i <= k+1;i ++)
        printf("%d\n",c[i]);
    return 0;
}

 

posted @ 2019-03-22 11:48  冥想选手  阅读(165)  评论(0编辑  收藏  举报