P1197 [JSOI2008]星球大战(并查集 删边->加边)

题目传送门

题意

给定n个星球,m条边,然后k次击毁星球,击毁星球的同时会把该星球与其他星球的联通的边一同删除,问每次操作后有多少个连通块。

输入格式

第一行包含两个整数,n,m 分别表示星球的数目和边的数目。星球用 0~n-1 整数编号。
接下来的 m 行,每行包括两个整数 x,y,表示星球 x 和星球 y 之间有边联通。
接下来的一行为一个整数 k,
接下来的 k 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 k 个数互不相同,且都在 0 到 n-1 的范围内。

输出格式

第一行是开始时星球的连通块个数。
接下来的 k 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

样例

input

8 13
0 1
1 6
6 5
5 0
0 6
1 2
2 3
3 4
4 5
7 1
7 2
7 6
3 6
5
1
6
3
5
7

output

1
1
1
2
3
3

思路

很显然,正着删边非常难维护,那么于是我们倒着加边,经典老套路。

code

#include <bits/stdc++.h>
using  namespace  std;

typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}

const int INF=0x3f3f3f3f;//2147483647;
const int N=4e5+50,M=1e5+50;
const ll mod=998244353;

int n,m;

int fa[N];
void init(){
    for(int i=0;i<=n;i++)fa[i]=i;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int ans;
vector<int>to[N];
int q[N];
int res[N];
int vis[N];
void solve() {
    cin>>n>>m;
    init();
    ans=n;
    for(int i=1;i<=m;i++){
        int u,v;cin>>u>>v;
        to[u].pb(v);
        to[v].pb(u);
    }
    int k;cin>>k;
    for(int i=1;i<=k;i++){
        cin>>q[i];
        vis[q[i]]=1;
    }
    for(int i=0;i<n;i++){
        if(!vis[i]){
            //cout<<i<<" ";
            for(auto v:to[i]){
                if(!vis[v]){
                    int a=find(i),b=find(v);
                    if(a!=b){
                        fa[a]=b;
                        ans--;
                    }
                }
            }
        }
    }
    ans-=k;
    res[k+1]=ans;
    for(int i=k;i>=1;i--){
        ans++;
        int u=q[i];vis[u]=0;
        for(auto v:to[u]){
            if(vis[v])continue;
            int a=find(u),b=find(v);
            if(a!=b){
                fa[a]=b;
                ans--;
            }
        }
        res[i]=ans;
    }
    for(int i=1;i<=k+1;i++){
        cout<<res[i]<<"\n";
    }
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int __=1;//cin>>__;
    while(__--){
        solve();
    }
    return 0;
}
posted @   illume  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示