题解:CF1633E Spanning Tree Queries

我们可以大概意识到, 如果将询问的 \(x\) 按顺序进行查询的话会出现一些性质.

但这不是非常明确, 而且观察到不能对 \(10^8\) 个值全部计算 MST.

我们接下来要考虑想办法选取一些关键的 \(x\) 值进行计算, 我们想要找到关键的少数 \(x\), 然后对这些值计算.

我们研究 Kruskal 算法中边被处理的先后顺序.

考虑一对边, 边权 \(w_1\), \(w_2\), 令 \(w_1 \lt w_2\).

\(x\)\(0\) 开始不断增大的过程中, 总是存在一个 \(x\) 会使得 \(w_2\)\(x\) 更近, \(w_2\) 在此及之后会先被考虑.

\(w_1 \leqslant x \leqslant w_2\) 中会出现这个分界线.

解一下.

\(x \leqslant \frac{w_1 + w_2}{2}\) 时, 选 \(w_1\) 优.

\(x \geqslant \lfloor \frac{w_1 + w_2}{2} \rfloor + 1\) 时, 选 \(w_2\) 优.

最开始是选 \(w_1\) 优的, 所以将 \(\lfloor \frac{w_1 + w_2}{2} \rfloor + 1\) 设为分界线.

踩到分界线的时候会交换 \(w_1\)\(w_2\) 的顺序, 这时对 MST 有影响, 需要重新计算.

不踩到分界线的时候, 不用交换.

同时考虑, 因为绝对值的原因, 所有给定的边权也需要设成分界线.

所以只有 \(O(m^2)\) 条分界线, 对每个分界线处用 Kruskal 做一次 MST.

查询的时候看给定的询问的 \(x\) 最后踩到的分界线是谁, 用这个分界线处的答案计算即可.

在这个题中, 我们需要注意:

  1. 如果需要多个值进行多次计算的时候, 观察一下是不是有很多值处的处理方式或答案构成是一样的, 只需要选取几个对答案有关键影响的关键点分界线处计算即可.

  2. 在考虑分界线形成的时候, 需要细化考虑, 将答案的贡献放到每一对边上, 这样可以方便找到分界线的位置.

\(Code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <tuple>
using namespace std;

const int NR = 60;
const int MR = 310;
const int TR = 1e5 + 10;

tuple<int, int, long long> e[MR];

long long b[TR];
int sz;
long long res[TR];
int num[TR];

int f[NR];

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

bool merge(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y) return false;
    f[x] = y;
    return true;
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i ++)
    {
        auto &[x, y, w] = e[i];
        scanf("%d%d%lld", &x, &y, &w);
    }
    b[++sz] = 0;
    for (int i = 1; i <= m; i ++)
    {
        b[++sz] = get<2>(e[i]);
        for (int j = 1; j < i; j ++)
        {
            b[++sz] = (get<2>(e[i]) + get<2>(e[j])) / 2 + 1;
        }
    }
    sort(b + 1, b + sz + 1);
    sz = unique(b + 1, b + sz + 1) - b - 1;
    for (int cur = 1; cur <= sz; cur ++)
    {
        sort(e + 1, e + m + 1, [&](const tuple<int, int, long long> &A, const tuple<int, int, long long> &B)
        {
            return abs(get<2>(A) - b[cur]) < abs(get<2>(B) - b[cur]);
        });
        for (int i = 1; i <= n; i ++)
        {
            f[i] = i;
        }
        int cnt = 0;
        for (int i = 1; i <= m; i ++)
        {
            auto [x, y, w] = e[i];
            if (find(x) == find(y)) continue;
            merge(x, y);
            if (w > b[cur])
            {
                res[cur] += w;
                num[cur] ++;
            }
            else res[cur] -= w;
            cnt ++;
            if (cnt == n - 1) break;
        }
    }
    int P, Q;
    long long A, B, C;
    scanf("%d%d%lld%lld%lld", &P, &Q, &A, &B, &C);
    long long lstx = 0;
    long long ans = 0;
    for (int i = 1; i <= Q; i ++)
    {
        long long x;
        if (i <= P) scanf("%lld", &x);
        else x = (lstx * A + B) % C;
        lstx = x;
        int pos = upper_bound(b + 1, b + sz + 1, x) - b - 1;
        long long now = res[pos] - x * num[pos] + x * (n - 1 - num[pos]);
        ans ^= now;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2026-03-18 11:36  hsy8116  阅读(6)  评论(0)    收藏  举报