P1197 [JSOI2008]星球大战(逆向思维,并查集)

题意:

给你一个无向图,每次从图中删去一个点,询问每次删点过后图中连通块的数量

思路:

本题可以离线,因此我们采用离线的逆向做法

怎么个逆向呢?我们假设一开始只有所有删点操作之后的点,并算出连通块个数

之后每次向图中加入被删除的点,并统计连通块个数

如果重新对所有点跑一遍的话时间复杂度上一定会炸,对于新加入的点,我们先对当前连通块个数加1,如果遍历该点连接的所有点,如果能够合并,那么就将连通块个数减1

最后把答案倒序输出就好了

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>
 using namespace std;
 const int maxn=4e5+1000;
 int flag[maxn],fa[maxn];
 vector<int>a[maxn];
 stack<int> s,q;
 int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
 int main()
 {
     int n,m,k,u,v;
     memset(flag,1,sizeof(flag));
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++){
         scanf("%d%d",&u,&v);
         a[u].push_back(v);
         a[v].push_back(u);
     }
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d",&u);
        s.push(u);
        flag[u]=0;
    }
    int cnt=n-k; 
    for(int i=0;i<n;i++) fa[i]=i;
    for(int i=0;i<n;i++){
        if(!flag[i])    continue;
        else{
            for(int j=0;j<a[i].size();j++){
                if(flag[a[i][j]]){
                    int f1=find(a[i][j]),f2=find(i);
                    if(f1!=f2)    fa[f1]=find(fa[f2]),cnt--;
                }
            }
        }
    }
    q.push(cnt);
    for(int i=1;i<=k;i++){
        cnt++;
        int x=s.top();
        s.pop();
        for(int j=0;j<a[x].size();j++){
            if(flag[a[x][j]]){
                int f1=find(a[x][j]),f2=find(x);
                if(f1!=f2)    fa[f1]=find(fa[f2]),cnt--;
            }
        }
        flag[x]=1;
        q.push(cnt);
    }
    while(!q.empty()){
        cout<<q.top()<<endl;
        q.pop();
    }
    return 0;
 }

猜你喜欢

转载自www.cnblogs.com/overrate-wsj/p/12291462.html