2023-6-15

D. Connected Components

题意:

给你 \(n\) 个点 \(m\) 条无向边,\(k\) 次询问,每次给你 \(l,r\) ,问你吧所有编号从 \([l,r]\) 的边删除之后
图里面还有几个连通块?

数据范围:

\(1\leq n \leq 500,1\leq m\leq 2*10^4,1\leq k \leq 2*10^4\)

\(Tutorial:\)

首先对于多组询问,查询一段区间的信息的,我们可以想到前后缀分解,我们可以开 \(m\) 个前后缀并查集维护
只用\([1,i]\) 或者 \([i+1,n]\) ,这些边并查集的合并情况,最后查询的时候我们在用 \(O(n)\) 复杂度去合并
\([1,l-1]\)\([r+1,n]\) 的信息即可,总体复杂度就是 \(O(m*n+k*n)\)


\(Code:\)

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


const int M = 1e4 + 10, N = 510;

int pre[M][N], suf[M][N];
array<int ,2> edge[M];
int f[N];

int find(int x,int *p) {
    return x == p[x] ? x : p[x] = find(p[x], p);
}

int main() {

    ios::sync_with_stdio(false);
    cin.tie(nullptr);


    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= m; i++) {
        auto &[x, y] = edge[i];
        cin >> x >> y;
    }

    for (int i = 1; i <= n; i++) {
        pre[0][i] = i;
        suf[m + 1][i] = i;
    }

    for (int i = 1; i <= m; i++) {
        auto [x, y] = edge[i];
        for (int j = 1; j <= n; j++) {
            pre[i][j] = pre[i - 1][j];
        }
        pre[i][find(x, pre[i])] = pre[i][find(y, pre[i])];
    }

    for (int i = m; i >= 1; i--) {
        auto [x, y] = edge[i];
        for (int j = 1; j <= n; j++) {
            suf[i][j] = suf[i + 1][j];
        }
        suf[i][find(x, suf[i])] = suf[i][find(y, suf[i])];
    }

    
    int k;
    cin >> k;
    while (k--) {
        int l, r;
        cin >> l >> r;
        for (int i = 1; i <= n; i++) {
            f[i] = i;
        }
        for (int i = 1; i <= n; i++) {
            f[find(i, f)] = find(find(i, pre[l - 1]), f);
            f[find(i, f)] = find(find(i, suf[r + 1]), f);
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            res += (f[i] == i);
        }
        cout << res << "\n";
    }

    return 0;
}
posted @ 2023-06-15 23:58  jackle  阅读(1)  评论(0编辑  收藏  举报