abc235_e MST + 1 题解

MST + 1

题意

给定一个 \(n\) 个点 \(m\) 条边的无向连通图,第 \(i\) 条边连接 \(x_i\)\(y_i\),边权为 \(c_i\)

\(Q\) 组询问,每组询问给定三个数 \(u_i\)\(v_i\)\(w_i\),令 \(e_i\) 为一条连接 \(u_i\)\(v_i\) 的无向边,边权为 \(w_i\)

对于每组询问,请你判断:对于加入这条边后的图,问其最小生成树中是否包含了这条边,如果是,输出Yes,否则输出No

保证题目给定的原图边权两两不同,保证加入的边的边权与原图中的任意一条边都不相同。

每组询问是独立的,互相不产生影响。

思路

使用克鲁斯卡尔算法。

首先这题数据范围不小,直接模拟是肯定不行的。

我们知道,克鲁斯卡尔算法是按边权从小到大排序,然后用并查集判断这条边是否有必要加入最小生成树,而每条边能否加入最小生成树中,取决于那些边权比这条边小的边

同理,每组询问中给出的边,只取决于那些原图中的边权比其小的边。

那么就可以推出一种做法:

  • 首先将每个询问给出的边和原图中的边共同记录下来。
  • 给所有边按边权从小到大排序。
  • 就像普通的最小生成树一样处理,只是那些询问中的边只需要判断、不需要加入图中

最后输出即可。

我的做法是离线做法,在线做法已经有人讲过了,详见这篇。

Code

点击查看代码
#include <iostream>
#include <algorithm>

using namespace std;

const int M = 4e5 + 10; // 记得开两倍空间

struct Node {
  int x, y, z, f; // 记录每条边的边权等,f 用来记录是第几次询问的边
  bool operator < (const Node &i) const {
    return z < i.z;
  }
} a[M];

int n, m, q, ans[M], f[M];

int Find (int x) {
  return (f[x] ? f[x] = Find(f[x]) : x);
}

int main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> q;
  for (int i = 1; i <= m; i++) {
    cin >> a[i].x >> a[i].y >> a[i].z;
  }
  for (int i = 1; i <= q; i++) {
    cin >> a[i + m].x >> a[i + m].y >> a[i + m].z; // 将所有边记录在一起
    a[i + m].f = i; // 记录是第几次询问
  }
  sort(a + 1, a + m + q + 1);
  for (int i = 1; i <= m + q; i++) {
    if (a[i].f) { // 如果不是原图中的边
      int l = Find(a[i].x), r = Find(a[i].y);
      ans[a[i].f] = (l != r); // 只需判断,不需加入
    } else { // 是原图中的边
      // 克鲁斯卡尔算法
      int l = Find(a[i].x), r = Find(a[i].y);
      if (l != r) {
        f[l] = r;
      }
    }
  }
  for (int i = 1; i <= q; i++) { // 输出
    cout << (ans[i] ? "Yes" : "No") << '\n';
  }
  return 0;
}
posted @ 2023-05-17 21:34  wnsyou  阅读(14)  评论(0编辑  收藏  举报