「Ynoi2004」rpmtdq

带权无根树,每次查询 \(\min\limits_{l \leq i < j \leq r} {dis(i, j)}\)\(1 \leq n \leq 2 \times 10^5, 1 \leq q \leq 10^6\)

区间所有点对最小值,考虑历史版本和、支配对等等,这道题使用支配对,但直接做没头绪,联系查询距离相关,所以考虑点分治,在操作以 \(u\) 为根子树时,由于区间取最小值,所以我们可以直接将 \(dis(u, i) + dis(u, j)\) 当成此时 \(dis(i, j)\),可以证明不影响答案,把某点到 \(u\) 的距离作为其权值 \(a\),然后拿出整棵子树结点并按编号排序,容易观察到此时若有 \(i < j < k\)\(a_j > \max(a_i, a_k)\) 则点对 \((i, k)\) 无效,因为由 \(\min(a_i, a_k)\) 对应那个点与 \(j\) 组成的点对构成区间更小,权值更小,支配了 \((i, k)\),然后思考点对数量,发现若 \(u\) 子树大小为 \(O(S)\) 的,则点对数量也是 \(O(S)\) 的,所以总的点对数量是 \(O(N \log N)\) 的(证明留给读者思考)。

然后找支配对,其实把上面无效条件写出来后就比较好想了,反过来点对 \((i, j)\) 有效的条件就是 \(\min\limits_{i < u < j} a_u > \max(a_i, a_j)\),这个正反两遍单调栈就做完了(也变相证明了点对数量上限)。

查询答案扫描线即可。

/*
address:https://www.luogu.com.cn/problem/P9058
AC 2026/9/17 12:42
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, LL> pil;
typedef pair<int, int> pii;
#define mkp make_pair
const int N = 2e5 + 5, Q = 1e6 + 5;
const LL INF = 1e18;
struct edge { int to, w; };
vector<edge>G[N];
int n, q;
inline void read(int& x) {
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
}
int siz[N];
bool vis[N];
inline int find(int u, int fa, int n) {
    siz[u] = 1;
    bool half = true;
    for (auto [v, w] : G[u])
        if (v != fa && !vis[v]) {
            int ret = find(v, u, n);
            if (ret) return ret;
            half &= siz[v] <= n >> 1;
            siz[u] += siz[v];
        }
    if (half && n - siz[u] <= n >> 1) return u;
    return 0;
}
inline int get_siz(int u, int fa) {
    int ret = 1;
    for (auto [v, w] : G[u])
        if (v != fa && !vis[v]) ret += get_siz(v, u);
    return ret;
}
LL a[N];
vector<int>node;
inline void assign(int u, int fa) {
    node.push_back(u);
    for (auto [v, w] : G[u])
        if (v != fa && !vis[v]) {
            a[v] = a[u] + w;
            assign(v, u);
        }
}
vector<pil>vec[N];
int stk[N], top;
inline void dfs(int u, int fa) {
    node.clear();
    int s = get_siz(u, fa);
    u = find(u, fa, s);
    vis[u] = true;
    a[u] = 0;
    assign(u, fa);
    sort(node.begin(), node.end());
    top = 0;
    for (int v : node) {
        while (top > 0 && a[v] < a[stk[top]]) --top;
        if (top) vec[max(v, stk[top])].push_back(mkp(min(v, stk[top]), a[v] + a[stk[top]]));
        stk[++top] = v;
    }
    top = 0;
    reverse(node.begin(), node.end());
    for (int v : node) {
        while (top > 0 && a[v] < a[stk[top]]) --top;
        if (top) vec[max(v, stk[top])].push_back(mkp(min(v, stk[top]), a[v] + a[stk[top]]));
        stk[++top] = v;
    }
    for (auto [v, w] : G[u])
        if (v != fa && !vis[v]) dfs(v, u);
}
vector<pii>qry[N];
inline void trans(LL& x, LL y) { x = x > y ? y : x; }
struct FenwickTree {
#define lowbit(x) (x & -x)
    LL c[N];
    inline void init(int n) { fill(c, c + n + 1, INF); }
    inline void change(int x, LL k) { for (;x <= n;x += lowbit(x)) trans(c[x], k); }
    inline LL query(int x) {
        LL ret = INF;
        for (;x > 0;x -= lowbit(x)) trans(ret, c[x]);
        return ret;
    }
}BIT;
LL ans[Q];
int main() {
    read(n);
    for (int i = 1;i < n;++i) {
        int u, v, w;read(u), read(v), read(w);
        G[u].push_back({ v, w });
        G[v].push_back({ u, w });
    }
    dfs(1, 0);
    read(q);
    for (int i = 1, l, r;i <= q;++i) {
        read(l), read(r);
        ans[i] = -1;
        if (l < r) qry[r].push_back(mkp(l, i));
    }
    BIT.init(n);
    for (int i = 1;i <= n;++i) {
        for (auto [j, d] : vec[i]) BIT.change(n - j + 1, d);
        for (auto [l, id] : qry[i]) ans[id] = BIT.query(n - l + 1);
    }
    for (int i = 1;i <= q;++i) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2026-09-17 18:02  keysky  阅读(3)  评论(0)    收藏  举报