1 条题解

  • 0
    @ 2026-10-27 12:58:44

    思路与算法

    用树直径两端的距离求每点到最远叶子的距离d,再计算安全值。重链剖分把u到v拆成按行进方向排列的区间,线段树存区间最大安全值;依次跳过最大值不足阈值的区间,在首个可行区间按相应方向找第一个点。

    正确性说明

    正权树上最远点可取叶子,且任一点的最远距离等于到直径两端距离的较大者,故安全值正确。树链分段按u到v的顺序覆盖路径且无遗漏;区间最大值不足阈值时其中无答案。首个可行区间内方向优先的线段树下降返回该段最早的合格点,所以也是整条路径最早的合格点。

    复杂度

    三次树遍历、重链与线段树初始化均O(n),单查询O(log²n),总O(n+m log²n),空间O(n)。

    易错点

    路径有方向,u到v和v到u答案可能不同,区间方向要分别记录。包括两端和单点路径;不存在返回-1。距离和(d+A)B先用64位再取模;深链用迭代遍历避免栈溢出。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        ll A, B, C;
        cin >> n >> m >> A >> B >> C;
        vector<vector<pair<int, int>>> adj(n);
        for (int i = 1, u, v, w; i < n; i++) {
            cin >> u >> v >> w;
            --u;
            --v;
            adj[u].push_back({v, w});
            adj[v].push_back({u, w});
        }
        auto distances = [&](int source) {
            vector<ll> d(n, -1);
            vector<int> q{source};
            d[source] = 0;
            for (size_t i = 0; i < q.size(); i++)
                for (auto e : adj[q[i]])
                    if (d[e.first] < 0) {
                        d[e.first] = d[q[i]] + e.second;
                        q.push_back(e.first);
                    }
            return d;
        };
        auto d0 = distances(0);
        int left = max_element(d0.begin(), d0.end()) - d0.begin();
        auto dl = distances(left);
        int right = max_element(dl.begin(), dl.end()) - dl.begin();
        auto dr = distances(right);
        vector<int> value(n);
        for (int i = 0; i < n; i++)
            value[i] = ((max(dl[i], dr[i]) + A) * B) % C;
        vector<int> parent(n, -1), depth(n), size(n, 1), heavy(n, -1), order{0};
        for (size_t i = 0; i < order.size(); i++)
            for (auto e : adj[order[i]])
                if (e.first != parent[order[i]]) {
                    parent[e.first] = order[i];
                    depth[e.first] = depth[order[i]] + 1;
                    order.push_back(e.first);
                }
        for (int i = n - 1; i > 0; i--) {
            int u = order[i], p = parent[u];
            size[p] += size[u];
            if (heavy[p] < 0 || size[u] > size[heavy[p]])
                heavy[p] = u;
        }
        vector<int> head(n), pos(n), at(n);
        vector<pair<int, int>> todo{{0, 0}};
        int timer = 0;
        while (!todo.empty()) {
            auto item = todo.back();
            todo.pop_back();
            for (int u = item.first; u >= 0; u = heavy[u]) {
                head[u] = item.second;
                pos[u] = timer;
                at[timer++] = u;
                for (auto e : adj[u])
                    if (parent[e.first] == u && e.first != heavy[u])
                        todo.push_back({e.first, e.first});
            }
        }
        int S = 1;
        while (S < n)
            S *= 2;
        vector<int> tree(2 * S, -1);
        for (int i = 0; i < n; i++)
            tree[S + pos[i]] = value[i];
        for (int i = S - 1; i; i--)
            tree[i] = max(tree[2 * i], tree[2 * i + 1]);
        function<int(int, int, int, int, int, int, bool)> first = [&](int p, int L, int R, int l, int r,
                                                                      int limit, bool reverse) {
            if (r < L || R < l || tree[p] < limit)
                return -1;
            if (L == R)
                return at[L];
            int middle = (L + R) / 2;
            int answer;
            if (reverse) {
                answer = first(p * 2 + 1, middle + 1, R, l, r, limit, reverse);
                if (answer < 0)
                    answer = first(p * 2, L, middle, l, r, limit, reverse);
            } else {
                answer = first(p * 2, L, middle, l, r, limit, reverse);
                if (answer < 0)
                    answer = first(p * 2 + 1, middle + 1, R, l, r, limit, reverse);
            }
            return answer;
        };
        while (m--) {
            int u, v, limit;
            cin >> u >> v >> limit;
            --u;
            --v;
            vector<array<int, 3>> segments, tail;
            while (head[u] != head[v]) {
                if (depth[head[u]] >= depth[head[v]]) {
                    segments.push_back({pos[head[u]], pos[u], 1});
                    u = parent[head[u]];
                } else {
                    tail.push_back({pos[head[v]], pos[v], 0});
                    v = parent[head[v]];
                }
            }
            if (depth[u] >= depth[v])
                segments.push_back({pos[v], pos[u], 1});
            else
                segments.push_back({pos[u], pos[v], 0});
            for (auto it = tail.rbegin(); it != tail.rend(); it++)
                segments.push_back(*it);
            int answer = -1;
            for (auto s : segments) {
                answer = first(1, 0, S - 1, s[0], s[1], limit, s[2]);
                if (answer >= 0)
                    break;
            }
            cout << (answer < 0 ? -1 : answer + 1) << '\n';
        }
    }
    
    • 1

    信息

    ID
    2084
    时间
    1000ms
    内存
    128MiB
    难度
    提高
    标签
    递交数
    0
    已通过
    0
    上传者