[lnsyoj1015/luoguP1197/JSOI2008]星球大战starwar

题意

给出一个 \(n\) 个点,\(m\) 条边的无向图,对其进行 \(k\) 次操作,每次操作会删除一个当前无向图中存在的点及其相邻的边,求原图 和每次操作之后的图的连通块个数

sol

由于需要计算连通块个数,可以自然的想到使用并查集解决。然而,删除某个点后,我们无法通过并查集快速地得知其与其他点是否直接联通,也就无法快速地计算。
考虑按照操作的逆序来计算,那么就相当于在一个 \(n-k\) 个点的无向图中,每次操作添加一个点,并添加一些与其相邻的边,求原图和每次操作之后的图的连通块个数。这样,就可以通过并查集来解决这个问题了。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <vector>

#define x first 
#define y second 

using namespace std;
typedef pair<int, int> PII;

const int N = 400005;

int n, m, k;
int g[N];
PII edges[N];
unordered_map<int, int> depth;
vector<PII> edge[N];
int fa[N];
int ans[N];

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

int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i ++ ) {
        int x, y;
        scanf("%d%d", &x, &y);
        edges[i] = {x, y};
    }

    scanf("%d", &k);
    for (int i = 1; i <= k; i ++ ) {
        scanf("%d", &g[i]);
        depth[g[i]] = k - i + 1;
    }
    
    for (int i = 1; i <= m; i ++ ) {
        int dx = depth[edges[i].x], dy = depth[edges[i].y];
        edge[max(dx, dy)].push_back(edges[i]);
    }

    for (int i = 1; i <= n; i ++ ) fa[i] = i;
    int cnt = n - k;

    for (int d = 0; d <= k; d ++ , cnt ++ ){
        // printf("#depth: %d\n", d);
        for (auto e : edge[d]){
            // printf("%d %d\n", e.x, e.y);
            int fx = find(e.x), fy = find(e.y);
            if (fx == fy) continue;
            cnt -- ;
            fa[fx] = fy;
        }

        ans[d] = cnt;
    }

    for (int i = k; i >= 0; i -- ) printf("%d\n", ans[i]);

    return 0;
}
posted @ 2024-09-28 17:05  是一只小蒟蒻呀  阅读(4)  评论(0编辑  收藏  举报