NC235745 拆路

题目链接

题目

题目描述

\(n\) 个城镇,城镇之间有 \(m\) 条道路相连,道路可以看成无向边。每一个城镇都有自己的一个繁荣度 \(v_i\) ,一个城镇 \(u\) 受到的影响 \(p\) 是与\(u\) 直接或者间接相连的所有城镇中,繁荣度的最大值。一个城镇 \(u\) 与城镇 \(v\) 是被视为直接或者间接相连的,当且仅当 \(u=v\) 或者从 \(u\) 出发,可以沿着某些道路到达 \(v\) 。为了减少维护成本,现准备拆除其中的某一些路。具体来说,你需要维护以下两种操作:

  1. ​ 'Q' \(a\),询问 \(a\) 城镇受到的影响 \(p\)
  2. ​ 'D' \(a\ b\) ,删除 \(a\ b\) 之间的道路。

输入描述

第一行输入两个整数 \(n,m(1\le n,m \le 10^5)\) ,分别表示城镇的数量和道路的数量。第二行输入 \(n\) 个整数 \(v_1,v_2,...,v_n(1\le v_i \le 10^9)\) ,分别表示每一个城镇的繁荣度。接下来 \(m\) 行,每行两个整数 \(u,v(1\le u,v \le n)\) ,表示城镇 \(u,v\) 之间有一条道路连接。保证不含有重边、自环。
接下来一行,输入一个整数 \(Q(1\le Q \le 10^5)\) ,表示操作的个数。
接下来 \(Q\) 行,每行描述一个操作,以'Q' \(a(1\le a \le n)\) 或者'D' \(a\ b(1\le a,b \le n)\) 的形式给出。对于删除操作,保证被删除的道路是存在的。

输出描述

对于每一个操作1,你都需要输出一个整数 \(p\) ,表示城镇 \(a\) 受到的影响。

示例1

输入

4 3
1 2 3 4
1 2
2 3
3 4
4
Q 1
D 2 3
Q 1
Q 3

输出

4
2
4

题解

知识点:并查集,离线。

普通并查集不支持撤销操作,这使得这道题难以解答,但转换一下思路,撤销的逆过程是新增,是并查集适用的操作,于是可以保存所有访问数据,离线逆向处理,再将答案保存最后输出即可。

时间复杂度 \(O((m \log n + q)\log q + n)\)

空间复杂度 \(O(q+n+m)\)

代码

#include <bits/stdc++.h>

using namespace std;

int w[100007];
int u[100007], v[100007];
int du[100007], dv[100007];
set<pair<int, int>> s;
int fa[100007];
int ans[100007];

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

void merge(int x, int y) {
    int rx = find(x);
    int ry = find(y);
    if (w[rx] < w[ry]) fa[rx] = ry;
    else fa[ry] = rx;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1;i <= n;i++) fa[i] = i;
    for (int i = 1;i <= n;i++) cin >> w[i];
    for (int i = 1;i <= m;i++) cin >> u[i] >> v[i];
    int q;
    cin >> q;
    for (int i = 1;i <= q;i++) {
        char op;
        cin >> op;
        if (op == 'Q') cin >> du[i];
        else if (op == 'D') {
            cin >> du[i] >> dv[i];
            s.insert({ du[i],dv[i] });
            s.insert({ dv[i],du[i] });
        }
    }
    for (int i = 1;i <= m;i++)
        if (s.find({ u[i],v[i] }) == s.end())
            merge(u[i], v[i]);
    for (int i = q;i >= 1;i--) {
        if (dv[i]) merge(du[i], dv[i]);
        else ans[i] = w[find(du[i])];
    }
    for (int i = 1;i <= q;i++) {
        if (ans[i]) cout << ans[i] << '\n';
    }
    return 0;
}
posted @ 2022-07-10 22:05  空白菌  阅读(58)  评论(0)    收藏  举报