P3242 接水果笔记

好题。

题意

给定一个 n 个结点的树,并且还给定了大小为 p 的一个带权路径集合 SS 中的路径 (x,y,v) 表示这是一条端点为 x,y 且权值为 v 的路径。

q 个询问,每个询问给出 (x,y),k,查询 S 中所有被 (x,y) 包含的路径中,权值第 k 小的路径。

n,p,q4×104

题解

考虑整体二分。

于是现在问题是:处理每个询问包含了几条给定路径。

考虑 (x,y) 包含 (u,v) 的条件,设 Lp,Rp 分别表示 p 的 dfn 序和 p 子树中最大的 dfn 序。

下面讨论 (x,y)(u,v) 的路径包含条件,钦定 Lx<Ly

  • LCA(u,v)=u,那么令 kvu 的路径上倒数第二个点,则充要条件是 (Lx<LkLx>Rk)(Ly[Lv,Rv])
  • LCA(u,v)u,那么充要条件是 (Lx[Lu,Ru])Ly[Lv,Rv]

转化一下就可以变成矩形覆盖问题。

于是现在,一个询问 (x,y) 所包含的路径数就是 (Lx,Ly) 被多少个矩形包含,这个可以用 K-D Tree 做。

代码:

code:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 4E4 + 5, LG = 21;
int n, p, tot, q, L[N], R[N], Ans[N], yf[N][LG], dep[N];
vector <int> G[N];
struct E {
  int typ, x, y, w, wtf;
  bool operator < (const E &l) const {return w < l.w;}
} e[N];
struct Q {
  int x, y, k, id;
} a[N], a1[N], a2[N];
void dfs(int x, int fa) {
  L[x] = ++tot;
  for (auto v : G[x]) {
    if (v == fa) continue;
    dep[v] = dep[x] + 1; yf[v][0] = x;
    for (int i = 1; i < LG; ++i)
      yf[v][i] = yf[yf[v][i - 1]][i - 1];
    dfs(v, x);
  } R[x] = tot;
}
int glca(int u, int v, bool cao = 0) {
  if (cao && u == v) return -1;
  if (dep[u] < dep[v]) swap(u, v);
  for (int i = LG - 1; i >= 0; --i)
    if (dep[u] - (1 << i) >= (cao ? dep[v] + 1 : dep[v])) u = yf[u][i];
  if (cao && yf[u][0] == v) return u;
  if (u == v) return u;
  for (int i = LG - 1; i >= 0; --i)
    if (yf[u][i] != yf[v][i]) u = yf[u][i], v = yf[v][i];
  return (cao ? u : yf[u][0]);
}
struct kdt {
  struct pos {int x[2], val;} po[N << 2], P;
  int b[N << 2], cnt, rt[LG], m;
  struct node {
    int ls, rs, sum;
    int L[2], R[2], x[2], val;
  } t[N << 2];
  void pushup(int x) {
    t[x].sum = t[t[x].ls].sum + t[t[x].rs].sum + t[x].val;
    for (int k : {0, 1}) {
      t[x].L[k] = t[x].R[k] = t[x].x[k];
      if (t[x].ls) {
        t[x].L[k] = min(t[x].L[k], t[t[x].ls].L[k]);
        t[x].R[k] = max(t[x].R[k], t[t[x].ls].R[k]);
      }
      if (t[x].rs) {
        t[x].L[k] = min(t[x].L[k], t[t[x].rs].L[k]);
        t[x].R[k] = max(t[x].R[k], t[t[x].rs].R[k]);
      }
    }
  }
  int build(int l, int r, int dep) {
    int mid = (l + r) >> 1;
    nth_element(b + l, b + mid, b + r + 1, [&](int x, int y) {return po[x].x[dep] < po[y].x[dep];});
    int x = b[mid]; t[x].x[0] = po[x].x[0];
    t[x].x[1] = po[x].x[1]; t[x].val = po[x].val;
    if (l < mid) t[x].ls = build(l, mid - 1, dep ^ 1);
    if (r > mid) t[x].rs = build(mid + 1, r, dep ^ 1);
    return pushup(x), x;
  }
  void append(int &x) {
    if (!x) return ; b[++cnt] = x;
    append(t[x].ls); append(t[x].rs);
    x = 0;
  }
  int query(int x) {
    if (!x) return 0;
    bool flg = 1;
    for (int k : {0, 1}) flg &= (t[x].R[k] <= P.x[k]);
    if (flg) return t[x].sum; 
    for (int k : {0, 1}) flg |= (t[x].L[k] > P.x[k]);
    if (flg) return 0; flg = 1;
    for (int k : {0, 1}) flg &= (t[x].x[k] <= P.x[k]);
    return query(t[x].ls) + query(t[x].rs) + flg * t[x].val;
  }
  void push(int x, int y, int val) {
    ++m; po[m].x[0] = x; po[m].x[1] = y;
    po[m].val = val; b[cnt = 1] = m;
    for (int i = 0; i < LG; ++i) {
      if (!rt[i]) {rt[i] = build(1, cnt, 0); break;}
      else append(rt[i]);
    }
  }
  void push(int lx, int rx, int ly, int ry) {
    if (rx < lx || ry < ly) return ;
    push(lx, ly, 1);
    push(lx, ry + 1, -1);
    push(rx + 1, ly, -1);
    push(rx + 1, ry + 1, 1);
  }
  int query(int x, int y) {
    P.x[0] = x, P.x[1] = y;
    int res = 0;
    for (int i = 0; i < LG; ++i) res += query(rt[i]);
    return res;
  }
  void clear() {
    for (int i = 0; i < LG; ++i) append(rt[i]);
    cnt = m = 0;
  }
} t;
void solve(int l, int r, int ql, int qr) {
  if (ql == qr) {
    for (int i = l; i <= r; ++i)
      Ans[a[i].id] = e[ql].w;
    return ;
  }
  int mid = (ql + qr) >> 1, cnt1 = 0, cnt2 = 0;  for (int i = ql; i <= mid; ++i) {
    int u = e[i].x, v = e[i].y;
    if (e[i].typ == 1) {
      t.push(1, L[u] - 1, L[v], R[v]);
      t.push(R[u] + 1, n, L[v], R[v]);
      t.push(L[v], R[v], 1, L[u] - 1);
      t.push(L[v], R[v], R[u] + 1, n);
    } else t.push(L[u], R[u], L[v], R[v]);
  }
  for (int i = l; i <= r; ++i) {
    int tmp = t.query(L[a[i].x], L[a[i].y]);
    if (tmp >= a[i].k) a1[++cnt1] = a[i];
    else a[i].k -= tmp, a2[++cnt2] = a[i];
  } t.clear();
  for (int i = 1; i <= cnt1; ++i)
    a[l + i - 1] = a1[i];
  for (int i = 1; i <= cnt2; ++i)
    a[l + cnt1 + i - 1] = a2[i];
  solve(l, l + cnt1 - 1, ql, mid);
  solve(l + cnt1, r, mid + 1, qr);
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);
  cin >> n >> p >> q;
  for (int i = 1; i < n; ++i) {
    int u, v; cin >> u >> v;
    G[u].emplace_back(v);
    G[v].emplace_back(u);
  } dep[1] = 1;
  for (int i = 0; i < LG; ++i) yf[1][i] = 1;
  dfs(1, 0);
  for (int i = 1; i <= p; ++i) {
    cin >> e[i].x >> e[i].y >> e[i].w;
    if (L[e[i].x] > L[e[i].y]) swap(e[i].x, e[i].y);
    e[i].typ = 1 + (glca(e[i].x, e[i].y) != e[i].x);
    if (e[i].typ == 1) e[i].x = glca(e[i].x, e[i].y, 1);
  }
  sort(e + 1, e + 1 + p);
  for (int i = 1; i <= q; ++i) {
    cin >> a[i].x >> a[i].y >> a[i].k;
    a[i].id = i;
    if (L[a[i].x] > L[a[i].y]) swap(a[i].x, a[i].y);
  } solve(1, q, 1, p);
  for (int i = 1; i <= q; ++i) cout << Ans[i] << '\n';
}

作者:CTHOOH

出处:https://www.cnblogs.com/CTHOOH/p/18056308

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   CTHOOH  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示