1 条题解

  • 0
    @ 2026-5-7 0:38:13

    dst(x,y)dst(x,y) 代表 xxyy 的距离,dphxdph_x 代表 xx 到根的距离。对于 uuvv 的路径,我们需要约束的是,对于路径上任何一个点 ii,都有 ai=ba_i=bT+dst(u,i)<tiT+dst(u,i)\lt t_i。记 uuvv 的 LCA 为 zz,我们把它拆成两部分:

    1. 对于路径 uuzz 上任何一个点 ii,有 ai=ba_i=bT+dphu<ti+dphiT+dph_u\lt t_i+dph_i

    2. 对于路径 vvzz 上任何一个点 ii,有 ai=ba_i=bT+dphu2×dphz<tidphiT+dph_u-2\times dph_z\lt t_i-dph_i

    注意到对于一次查询,左边都是相等的,所以只需要对一条链查询右边这个式子(ti+dphit_i+dph_itidphit_i-dph_i )在 aiba_i\not=b 时的最小值即可。为了做到这一点,我们维护其最小值、最小值颜色、不同于最小值颜色的最小值(即“次小值”)即可。

    使用斜二倍增优化,预处理复杂度 O(n)O(n),查询复杂度 O(logn)O(\log n)。实现细节方面,向上可以朴素做,向下可以(自下向上)倍增时记录最后一段不满足的,然后对着结构逐层向下即可。更详细的可以看代码。

    学习斜二倍增喵,学习斜二倍增谢谢喵

    #include <bits/stdc++.h>
    using namespace std;
    constexpr int N = 1e5 + 9;
    inline void cmin(int& x, int y) { x > y && (x = y); }
    struct info {
      int mn, mnc, mn2;
      int operator()(int c) const { return c == mnc ? mn2 : mn; }
      info& operator+=(info to) {
        if (mn > to.mn) swap(*this, to);
        return cmin(mn2, to(mnc)), *this;
      }
    } vl[2][N], mx[2][N];
    int n, m, d[N], fa[N], lb[N], dph[N], a[N];
    inline int lca(int u, int v) {
      if (d[u] < d[v]) swap(u, v);
      while (d[u] > d[v]) u = u[d[lb[u]] >= d[v] ? lb : fa];
      while (u != v)
        lb[u] != lb[v] ? (u = lb[u], v = lb[v]) : (u = fa[u], v = fa[v]);
      return u;
    }
    int qry1(int u, int z, int b, int t) {
      auto chk = [&](info x) { return x(b) > t; };
      while (d[u] >= d[z])
        if (chk(mx[0][u]))
          u = lb[u];
        else if (chk(vl[0][u]))
          u = fa[u];
        else
          return u;
      return 0;
    }
    int qry2(int u, int z, int b, int t) {
      auto chk = [&](info x) { return x(b) > t; };
      int x = 0;
      while (d[u] >= d[z])
        if (d[lb[u]] >= d[z])
          !chk(mx[1][u]) && (x = u), u = lb[u];
        else
          !chk(vl[1][u]) && (x = u), u = fa[u];
      if (d[lb[x]] >= d[z]) {
        while (lb[x] != fa[x]) {
          int p = fa[x], q = lb[p];
          if (!chk(mx[1][q]))
            x = q;
          else if (!chk(mx[1][p]))
            x = p;
          else
            break;
        }
      }
      return x;
    }
    signed main() {
      cin.tie(nullptr)->sync_with_stdio(false);
      cin >> n;
      for (int i = 2; i <= n; ++i) cin >> fa[i] >> dph[i], dph[i] += dph[fa[i]];
      for (int i = 1; i <= n; ++i) cin >> a[i];
      for (int i = 1, t; i <= n; ++i) {
        int p = fa[i], q = lb[p], r = lb[q];
        cin >> t, d[i] = d[p] + 1;
        mx[0][i] = vl[0][i] = {t + dph[i], a[i], INT_MAX};
        mx[1][i] = vl[1][i] = {t - dph[i], a[i], INT_MAX};
        if (!p || d[p] - d[q] != d[q] - d[r])
          lb[i] = p;
        else {
          lb[i] = r;
          mx[0][i] += mx[0][p], mx[0][i] += mx[0][q];
          mx[1][i] += mx[1][p], mx[1][i] += mx[1][q];
        }
      }
      for (cin >> m; m; --m) {
        int u, v, b, t, z;
        cin >> u >> v >> b >> t, z = lca(u, v);
        int ans = qry1(u, z, b, t += dph[u]) ?: qry2(v, z, b, t - (dph[z] << 1));
        cout << (ans ?: -1) << '\n';
      }
      return cout << flush, 0;
    }
    
    • 1

    「UOI 2021 Stage 4 Day1」树上的强盗

    信息

    ID
    10975
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者