Educational Codeforces Round 122 E. Spanning Tree Queries

原题链接

题目大意

可以一张\(n\)个节点\(m\)条边的无向图,对你进行\(k\)次询问,每次询问给你一个值\(x\),让你求出权值\(\displaystyle\sum_{i=1}^{n-1}|w_i-x|\)最小的最小生成树,输出所有询问结果的异或和

题目分析

求最小生成树,我们首先会想到Kruskal算法来求解,但是询问次数特别多,所以每次询问用Kruskal求最小生成树肯定会超时

考虑一下Kruskal算法的过程,如果每次询问的值\(x\)比原来增加1,但是权值的大小顺序没有发生改变,那么最小生成树边的选择一定不会改变,接下来我们只需要思考一下,\(x\)什么时候会改变权值的大小顺序

假设有边\(i,j\)权值为\(w_i,w_j\),当\(x\le \frac{w_i+w_j}{2}\)的时候,\(|x-w_i|\)和\(|x-w_j|\)的大小顺序不发生改变;而当\(x\ge \frac{w_i+w_j}{2}+1\)的时候,权值的大小顺序发生改变,那么最小生成树就有重新选边的必要了。所以我们可以将\(\frac{w_i+w_j}{2}+1\)作为分界点,让其对应一个最小生成树的选边情况。

这样一来,我们先求出这张图中任意两条边的分界点,看作是最小生成树的一种情况,并将该情况的所有边权存储下来,时间复杂度为\(O(m^2)\),数据量最坏为\(90000\)。通过二分来确定询问所在的区间,通过存下来的边权的情况,求出询问的结果,最后得到答案

代码

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

#define _range(container) container.begin(), container.end()
#define L_B lower_bound
#define U_B upper_bound

constexpr int N = 1e5 + 100;
int n, m;
int cur[N], p[N], cnt;
LL result[N], res;
VI ed, order[N];

struct Edge {
  int a, b, w;
} edge[N];

int _x;
inline bool cmp(Edge a, Edge b) {
  return abs(_x-a.w) < abs(_x-b.w);
}

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

int main() {
  scanf("%d%d", &n, &m);

  for (int i = 0; i < m; i++) {
    int a, b, c;
    scanf("%d%d%d", &a, &b, &c);
    edge[i] = {a, b, c};
  }

  for (int i = 1; i < m; i++)
    for (int j = 0; j < i; j++) {
      int mid = (edge[i].w + edge[j].w) / 2 + 1;
      ed.emplace_back(mid);
    }
  
  // 特殊情况,当询问的x=0,那么问题就变成求最小生成树
  ed.emplace_back(0);
  sort(_range(ed));

  int cnt = 0;
  for (auto x : ed) {
    _x = x;
    sort(edge, edge + m, cmp);
    for (int i = 0; i <= n + 1; i++) p[i] = i;

    for (int i = 0; i < m; i++) {
      int a = find(edge[i].a), b = find(edge[i].b), w = edge[i].w;
      if (a != b) {
        p[a] = b;
        order[cnt].emplace_back(w);
      }
    }
    cnt++;
  }

  int p, k, a, b, c, x;
  scanf("%d%d%d%d%d", &p, &k, &a, &b, &c);

  for (int i = 1; i <= k; i++) {
    if (i <= p) scanf("%d", &x);
    else x = (x * 1LL * a + b) % c;
   
    int t = U_B(_range(ed), x) - ed.begin() - 1;
    LL sum = 0;
    for (auto e : order[t]) 
      sum += abs(e - x);
    res ^= sum;
  }

  printf("%lld\n", res);

  return 0;
}

上一篇:链式前向星


下一篇:GNU Octave计算数据