P4175 [CTSC2008]网络管理(整体二分)

P4175 [CTSC2008]网络管理

给定一棵有 n n n个节点的树,点有点权,有两种操作:① 修改某个点的点权,② 查询两点路径间的点权第 k k k大。

给定 u , v u, v u,v,选定 1 1 1号节点为根节点,设 i n f ( x ) inf(x) inf(x)表示从根节点到点 x x x的信息,两点间的信息可以描述为 i n f ( u ) + i n f ( v ) − i n f ( l c a ( u , v ) ) − f a ( i n f ( l c a ( u , v ) ) ) inf(u) + inf(v) - inf(lca(u, v)) - fa(inf(lca(u, v))) inf(u)+inf(v)inf(lca(u,v))fa(inf(lca(u,v)))

由此我们可以把树上每个点拆成 [ l , r , x ] [l, r, x] [l,r,x] l l l为这个点进入 d f s dfs dfs d f s dfs dfs序, r r r为这个点走出 d f s dfs dfs d f s dfs dfs序,

我们要查找 i n f ( u ) inf(u) inf(u)上的信息就只要查找有多少个 [ l , r , x ] [l, r, x] [l,r,x]满足 l ≤ d f n ( u ) ≤ r l \leq dfn(u) \leq r ldfn(u)r,树状数组区间更新,单点查询就好了。

对于每个询问可以写成 u , v , l c a ( u , v ) , f a ( l c a ( u , v ) ) u, v, lca(u, v), fa(lca(u, v)) u,v,lca(u,v),fa(lca(u,v)),我们把树上的点,修改操作,询问操作放到数组中整体二分一下即可。

(感觉代码好像比树套树好写一点点……)

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 10;

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

int a[N], ans[N], n, m;

int son[N], fa[N], l[N], dep[N], r[N], sz[N], top[N], sum[N], tot;

struct Res {
  int l, r, f, ff, x, id, type;
}q[N], q1[N], q2[N];

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

void dfs1(int rt, int f) {
  dep[rt] = dep[f] + 1, fa[rt] = f, sz[rt] = 1, l[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];
    }
  }
  r[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 x, int y) {
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) {
      swap(x, y);
    }
    x = fa[top[x]];
  }
  return dep[x] <= dep[y] ? x : y;
}

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 solve(int l, int r, int L, int R) {
  if (l > r || L > R) {
    return ;
  }
  if (l == r) {
    for (int i = L; i <= R; i++) {
      if (q[i].type == 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].type == 1) {
      if (q[i].x <= mid) {
        int l = q[i].l, r = q[i].r;
        update(l, q[i].id), update(r + 1, -q[i].id);
        q1[++cnt1] = q[i];
      }
      else {
        q2[++cnt2] = q[i];
      }
    }
    else {
      int cur = query(q[i].l) + query(q[i].r) - query(q[i].f) - query(q[i].ff);
      if (cur >= q[i].x) {
        q1[++cnt1] = q[i];
      }
      else {
        q[i].x -= cur;
        q2[++cnt2] = q[i];
      }
    }
  }
  for (int i = 1; i <= cnt1; i++) {
    if (q1[i].type == 1) {
      int l = q1[i].l, r = q1[i].r;
      update(l, -q1[i].id), update(r + 1, q1[i].id);
    }
  }
  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, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  for (int i = 1, x, y; i < n; i++) {
    scanf("%d %d", &x, &y);
    add(x, y);
    add(y, x);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  for (int i = 1; i <= m; i++) {
    ans[i] = 1000000000;
  }
  int num = 0;
  for (int i = 1; i <= n; i++) {
    q[++num] = {l[i], r[i], 0, 0, a[i], 1, 1};
  }
  for (int i = 1, k, x, y; i <= m; i++) {
    scanf("%d %d %d", &k, &x, &y);
    if (!k) {
      q[++num] = {l[x], r[x], 0, 0, a[x], -1, 1};
      a[x] = y;
      q[++num] = {l[x], r[x], 0, 0, a[x], 1, 1};
    }
    else {
      int f = lca(x, y), ff = fa[f], sum = dep[x] + dep[y] - dep[f] - dep[ff];
      if (sum >= k) {
        q[++num] = {l[x], l[y], l[f], l[ff], sum - k + 1, i, 2};
      }
      else {
        ans[i] = -1;
      }
    }
  }
  solve(0, 100000000, 1, num);
  for (int i = 1; i <= m; i++) {
    if (ans[i] != 1000000000) {
      if (ans[i] == -1) {
        puts("invalid request!");
      }
      else {
        printf("%d\n", ans[i]);
      }
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值