P3242 [HNOI2015] 接水果(整体二分、扫描线)

P3242 [HNOI2015] 接水果

给定一棵树,定义给定了 p p p个盘子,每个盘子是树上 u , v u, v u,v两点的路径,且盘子有权值,定义水果,水果也是树上 u , v u, v u,v两点间的路径。

q q q个询问,每次给定 u , v , k u, v, k u,v,k,表示可以接住水果 u , v u, v u,v的盘子中权值第 k k k小的权值是什么,输出权值,一个盘子可以接住一个水果,当且仅当盘子是水果的子路径。

考虑如何求是否覆盖,对每个点 d f s dfs dfs序得到 [ s t i , e d i ] [st_i, ed_i] [sti,edi],对于 ( u , v ) , s t u ≤ s t v (u, v), st_u \le st_v (u,v),stustv

  • l c a ( u , v ) = u lca(u, v) = u lca(u,v)=u,则只要有一个点 d f s dfs dfs序在 [ s t v , e d v ] [st_v, ed_v] [stv,edv],并且有一个点 d f s dfs dfs序在 [ 1 , s t z − 1 ] , [ e d z + 1 , n ] [1, st_z - 1], [ed_z + 1, n] [1,stz1],[edz+1,n],即为内含,其中 z z z u , v u, v u,v路径上 u u u的儿子。
  • l c a ( u , v ) ≠ u lca(u, v) \ne u lca(u,v)=u,则只要有一个点 d f s dfs dfs序在 [ s t u , e d u ] [st_u, ed_u] [stu,edu],并且有一个点 d f s dfs dfs序在 [ s t v , e d v ] [st_v, ed_v] [stv,edv],即为内含。

可以把所有矩形的差分,类似扫描线离线一下,然后整体二分即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int head[N], to[N << 1], nex[N << 1], cnt = 1;

int son[N], dep[N], top[N], sz[N], st[N], ed[N], fa[N], tot;

int ans[N], sum[N], n, P, Q, num;

struct Res {
  int op, x, l, r, k, id;

  bool operator < (const Res &t) const {
    return x != t.x ? x < t.x : op < t.op;
  }
}q[N * 5], q1[N * 5], q2[N * 5];

inline int lowbit(int x) {
  return x & (-x);
}

void update(int rt, int v) {
  while (rt <= n) {
    sum[rt] += v;
    rt += lowbit(rt);
  }
}

int query(int rt) {
  int ans = 0;
  while (rt) {
    ans += sum[rt];
    rt -= lowbit(rt);
  }
  return ans;
}

void add(int x, int y) {
  to[cnt] = y;
  nex[cnt] = head[x];
  head[x] = cnt++;
}

void dfs1(int rt, int f) {
  fa[rt] = f, sz[rt] = 1, dep[rt] = dep[f] + 1, st[rt] = ++tot;
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == f) {
      continue;
    }
    dfs1(to[i], rt);
    sz[rt] += sz[to[i]];
    if (!son[rt] || sz[to[i]] > sz[son[rt]]) {
      son[rt] = to[i];
    }
  }
  ed[rt] = tot;
}

void dfs2(int rt, int tp) {
  top[rt] = tp;
  if (!son[rt]) {
    return ;
  }
  dfs2(son[rt], tp);
  for (int i = head[rt]; i; i = nex[i]) {
    if (to[i] == fa[rt] || to[i] == son[rt]) {
      continue;
    }
    dfs2(to[i], to[i]);
  }
}

int lca(int u, int v) {
  while (top[u] != top[v]) {
    if (dep[top[u]] < dep[top[v]]) {
      swap(u, v);
    }
    u = fa[top[u]];
  }
  return dep[u] < dep[v] ? u : v;
}

int get(int u, int v) {
  while (top[u] != top[v]) {
    if (fa[top[v]] == u) {
      return top[v];
    }
    v = fa[top[v]];
  }
  return son[u];
}

void solve(int L, int R, int l, int r) {
  if (L > R) {
    return ;
  }
  if (l == r) {
    for (int i = L; i <= R; i++) {
      if (q[i].op == 2) {
        ans[q[i].id] = l;
      }
    }
    return ;
  }
  int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
  for (int i = L; i <= R; i++) {
    if (q[i].op != 2) {
      if (q[i].k <= mid) {
        update(q[i].l, q[i].op), update(q[i].r + 1, -q[i].op);
        q1[++cnt1] = q[i];
      }
      else {
        q2[++cnt2] = q[i];
      }
    }
    else {
      int cur = query(q[i].l);
      if (cur >= q[i].k) {
        q1[++cnt1] = q[i];
      }
      else {
        q[i].k -= cur;
        q2[++cnt2] = q[i];
      }
    }
  }
  for (int i = 1; i <= cnt1; i++) {
    if (q1[i].op != 2) {
      update(q1[i].l, -q1[i].op), update(q1[i].r + 1, q1[i].op);
    }
  }
  for (int i = 1; i <= cnt1; i++) {
    q[L + i - 1] = q1[i];
  }
  for (int i = 1; i <= cnt2; i++) {
    q[L + cnt1 + i - 1] = q2[i];
  }
  solve(L, L + cnt1 - 1, l, mid), solve(L + cnt1, R, mid + 1, r);
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d %d %d", &n, &P, &Q);
  for (int i = 1, u, v; i < n; i++) {
    scanf("%d %d", &u, &v);
    add(u, v);
    add(v, u);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  for (int i = 1, u, v, x; i <= P; i++) {
    scanf("%d %d %d", &u, &v, &x);
    if (st[u] > st[v]) {
      swap(u, v);
    }
    // u < v;
    int cur = lca(u, v);
    if (cur == u) {
      int z = get(u, v);
      // [1, st[z] - 1]
      if (st[z] != 1) {
        q[++num] = {1, 1, st[v], ed[v], x, 0};
        q[++num] = {-1, st[z], st[v], ed[v], x, 0};
      }
      // [ed[z] + 1, n]
      if (ed[z] != n) {
        q[++num] = {1, st[v], ed[z] + 1, n, x, 0};
        q[++num] = {-1, ed[v] + 1, ed[z] + 1, n, x, 0};
      }
    }
    else {
      q[++num] = {1, st[u], st[v], ed[v], x, 0};
      q[++num] = {-1, ed[u] + 1, st[v], ed[v], x, 0};
    }
  }
  for (int i = 1, u, v, k; i <= Q; i++) {
    scanf("%d %d %d", &u, &v, &k);
    if (st[u] > st[v]) {
      swap(u, v);
    }
    q[++num] = {2, st[u], st[v], 0, k, i};
  }
  sort(q + 1, q + 1 + num);
  solve(1, num, 0, 1000000000);
  for (int i = 1; i <= Q; i++) {
    printf("%d\n", ans[i]);
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值