P3250 [HNOI2016]网络(利用堆建线段树 + 树剖)

P3250 [HNOI2016]网络

做法有点神奇!!!利用堆作为节点建立一颗线段树,用堆维护线段树上点的信息。

说说查询操作,我们的目的是要查询,没有经过这个点的事件最大值,考虑如何维护。

我们定义线段树上的信息,每个点记录的是没有经过这个区间的事件值。

事件插入,我们可以利用树剖得到从 x − > y x->y x>y的路径形成的 log ⁡ n \log n logn个连续的段,

我们先把这些段给存下来,然后再按照 l l l排个序,在这些段之外插入事件值。

同样的事件删除,我们只要在这些段之外删除事件值即可,这里好像有 ( log ⁡ n ) 3 (\log n) ^ 3 (logn)3

对于查询操作,我们只要去查找这个点所在的区间的最大值即可(因为我们记录的是不在这个区间里的值)。

#include <bits/stdc++.h>
#define mid (l + r >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define ls rt << 1
#define rs rt << 1 | 1

using namespace std;

const int N = 1e5 + 10;

struct Heap {
  priority_queue<int> a, b;

  void push(int x) {
    a.push(x);
  }

  void erase(int x) {
    b.push(x);
  }

  int top() {
    while (b.size() && a.top() == b.top()) {
      a.pop(), b.pop();
    }
    return a.top();
  }
}tree[N << 2];

void build(int rt, int l, int r) {
  tree[rt].push(-1);
  if (l == r) {
    return ;
  }
  build(lson);
  build(rson);
}

void update(int rt, int l, int r, int L, int R, int value, int op) {
  if (l >= L && r <= R) {
    if (op) {
      tree[rt].push(value);
    }
    else {
      tree[rt].erase(value);
    }
    return ;
  }
  if (L <= mid) {
    update(lson, L, R, value, op);
  }
  if (R > mid) {
    update(rson, L, R, value, op);
  }
}

int query(int rt, int l, int r, int L, int R) {
  if (l >= L && r <= R) {
    return tree[rt].top();
  }
  int ans = tree[rt].top();
  if (L <= mid) {
    ans = max(ans, query(lson, L, R));
  }
  if (R > mid) {
    ans = max(ans, query(rson, L, R));
  }
  return ans;
}

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

int fa[N], son[N], sz[N], top[N], dep[N], rk[N], id[N], tot;

int a[N << 1], b[N << 1], v[N << 1], n, m;

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, dep[rt] = dep[f] + 1, sz[rt] = 1;
  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[son[rt]] < sz[to[i]]) {
      son[rt] = to[i];
    }
  }
}

void dfs2(int rt, int tp) {
  top[rt] = tp, rk[++tot] = rt, id[rt] = tot;
  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]);
  }
}

typedef pair<int, int> pii;

pii sec[N];

void update(int x, int y, int v, int op) {
  int cnt = 0;
  while (top[x] != top[y]) {
    if (dep[top[x]] < dep[top[y]]) {
      swap(x, y);
    }
    sec[++cnt] = make_pair(id[top[x]], id[x]);
    x = fa[top[x]];
  }
  if (dep[x] > dep[y]) {
    swap(x, y);
  }
  sec[++cnt] = make_pair(id[x], id[y]);
  sort(sec + 1, sec + 1 + cnt);
  int l = 0;
  for (int i = 1; i <= cnt; i++) {
    if (l + 1 < sec[i].first) {
      update(1, 1, n, l + 1, sec[i].first - 1, v, op);
    }
    l = sec[i].second;
  }
  if (l < n) {
    update(1, 1, n, l + 1, n, v, op);
  }
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  scanf("%d %d", &n, &m);
  for (int i = 1; i < n; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    add(x, y);
    add(y, x);
  }
  dfs1(1, 0);
  dfs2(1, 1);
  build(1, 1, n);
  for (int i = 1; i <= m; i++) {
    int op, x;
    scanf("%d", &op);
    if (op == 0) {
      scanf("%d %d %d", &a[i], &b[i], &v[i]);
      update(a[i], b[i], v[i], 1);
    }
    else if (op == 1) {
      scanf("%d", &x);
      update(a[x], b[x], v[x], 0);
    }
    else {
      scanf("%d", &x);
      printf("%d\n", query(1, 1, n, id[x], id[x]));
    }
  }
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值