[bzoj3306]樹——樹上倍增+dfs序+線段樹


Brief Description

您需要寫一種數據結構,支持:

  1. 更改一個點的點權
  2. 求一個子樹的最小點權
  3. 換根

Algorithm Design

我們先忽略第三個要求。
看到要求子樹的最小點權,我們想到使用dfs序。容易看到,一個節點的子樹在dfs序中的范圍就是\([l(x),r(x)]\),所以我們把樹結構變成了線性結構,從而變成了一個RMQ問題,我們使用線段樹即可求解。
對於換根,我們不必重新求出拓撲結構。我們考察換根會影響到的節點。對於新根的子樹中的節點,一定沒有影響,對於不是新根祖先的節點,一定也沒有影響。所以我們只考慮新根的祖先。
我們可以看出,換成新根以后,祖先的覆蓋范圍就變成了全樹拋去新根到祖先路徑上距離祖先距離最近的節點的子樹大小,設這個點為y,那么dfs序中的范圍就是\([1,l[y]-1] \cup [r[y]+1, n]\)
所以問題就變成了如何求樹上離某個點距離最近的兒子。顯然可以使用樹上倍增來求。

Code

#include <algorithm>
#include <cstdio>
using std::max;
using std::min;
const int maxn = 100010;
int q[maxn], ind = 0, l[maxn], r[maxn], n, m, root, val[maxn], deep[maxn],
             fa[maxn][20];
int cnt = 0;
struct edge {
  int to, next;
} e[maxn];
int last[maxn];
struct seg {
  int l, r, mn;
} t[maxn << 2];
void insert(int x, int y) {
  e[++cnt].to = y;
  e[cnt].next = last[x];
  last[x] = cnt;
}
void update(int k) { t[k].mn = min(t[k << 1].mn, t[k << 1 | 1].mn); }
void dfs(int x) {
  l[x] = ++ind;
  q[ind] = x;
  for (int i = 1; i <= 16; i++) {
    if (deep[x] < (1 << i))
      break;
    fa[x][i] = fa[fa[x][i - 1]][i - 1];
  }
  for (int i = last[x]; i; i = e[i].next) {
    fa[e[i].to][0] = x;
    deep[e[i].to] = deep[x] + 1;
    dfs(e[i].to);
  }
  r[x] = ind;
}
void build(int k, int l, int r) {
  t[k].l = l, t[k].r = r;
  int mid = (l + r) >> 1;
  if (l == r) {
    t[k].mn = val[q[l]];
    return;
  }
  build(k << 1, l, mid);
  build(k << 1 | 1, mid + 1, r);
  t[k].mn = min(t[k << 1].mn, t[k << 1 | 1].mn);
}
void modify(int k, int pos, int val) {
  int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
  if (l == r) {
    t[k].mn = val;
    return;
  }
  if (pos <= mid)
    modify(k << 1, pos, val);
  else
    modify(k << 1 | 1, pos, val);
  update(k);
}
int query(int k, int x, int y) {
  int l = t[k].l, r = t[k].r, mid = (l + r) >> 1;
  if (x <= l && r <= y)
    return t[k].mn;
  int ans = 0x3f3f3f;
  if (x <= mid)
    ans = min(ans, query(k << 1, x, y));
  if (y > mid)
    ans = min(ans, query(k << 1 | 1, x, y));
  return ans;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("input", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; i++) {
    int f;
    scanf("%d %d", &f, &val[i]);
    if (f)
      insert(f, i);
  }
  dfs(root = 1);
#ifndef ONLINE_JUDGE
  for (int i = 1; i <= ind; i++)
    printf("%d ", q[i]);
  printf("\n");
#endif
  build(1, 1, n);
  while (m--) {
    char ch[5];
    int x;
    scanf("%s %d", ch, &x);
    if (ch[0] == 'V') {
      int val;
      scanf("%d", &val);
      modify(1, l[x], val);
    } else if (ch[0] == 'E')
      root = x;
    else {
      if (root == x)
        printf("%d\n", t[1].mn);
      else if (l[x] <= l[root] && r[x] >= r[root]) { // x is the father of root
        int y = root, d = deep[y] - deep[x] - 1;
        for (int i = 0; i <= 16; i++)
          if (d & (1 << i))
            y = fa[y][i];
        printf("%d\n", min(query(1, 1, l[y] - 1), query(1, r[y] + 1, n)));
      } else
        printf("%d\n", query(1, l[x], r[x]));
    }
  }
  return 0;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com