Loj 139. 树链剖分

知识点:树链剖分

原题面 Loj

简述

给定一棵 \(n\) 个节点的树,点有点权,初始时该树的根为 \(1\) 号节点。给定 \(m\) 次操作:

  1. 换根:将一个指定的节点设置为树的新根。
  2. 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
  3. 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
  4. 询问路径:询问某条路径上节点的权值和。
  5. 询问子树:询问某个子树内节点的权值和。

\(1\le n,m\le 10^5\)\(0\le\) 点权、修改量\(\le 10^5\)
1S,256MB。

分析

除去换根操作后就是板题了,钦定 1 为根轻重链剖分后用线段树维护 dfs 序即可,这里主要记录如何处理换根操作。

对于路径查询/修改操作,并不会被根的位置影响,套用之前的做法即可。

对于子树查询/修改操作,考虑对当前根 \(r\) 与指定节点 \(u\) 的位置关系分类讨论:

  • \(r=u\),直接对整棵树做操作。
  • \(r\) 在原树上不在 \(u\) 的子树内,换根后对 \(u\) 子树节点的 dfs 序没有影响,做法同根为 1 的情况,直接访问对应位置即可,即有下图所示:

\(r\) 在原树上不在 \(u\) 的子树内

  • \(u\) 在原树上在 \(r\) 的子树内,如下图所示。考虑补集转化,先操作整棵树,再减去以节点 \(5\) 为根子树的贡献。可以发现节点 \(5\) 即链 \(u\rightarrow r\) 上距离 \(u\) 最近的节点,找到这类节点时可以通过跳重链实现,详见代码。

\(r\) 在原树上在 \(u\) 的子树内

代码

//知识点:重链剖分,线段树 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kN = 1e5 + 10;
//=============================================================
int n, m, root, ori[kN], a[kN];
int e_num, head[kN], v[kN << 1], ne[kN << 1];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
void AddEdge(int u_, int v_) {
  v[++ e_num] = v_;
  ne[e_num] = head[u_];
  head[u_] = e_num;
}
namespace Seg {
  #define ls (now_<<1)
  #define rs (now_<<1|1)
  #define mid ((L_+R_)>>1)
  LL sum[kN << 2], tag[kN << 2];
  void Pushup(int now_) {
    sum[now_] = (sum[ls] + sum[rs]);
  }
  void Pushdown(int now_, int L_, int R_) {
    sum[ls] += 1ll * tag[now_] * (mid - L_ + 1);
    sum[rs] += 1ll * tag[now_] * (R_ - mid);
    tag[ls] += tag[now_]; 
    tag[rs] += tag[now_];
    tag[now_] = 0ll;
  }
  void Build(int now_, int L_, int R_) {
    if (L_ == R_) {
      sum[now_] = a[L_];
      tag[now_] = 0ll;
      return ;
    }
    Build(ls, L_, mid), Build(rs, mid + 1, R_);
    Pushup(now_); 
  }
  void Modify(int now_, int L_, int R_, int l_, int r_, LL val_) {
    if (l_ <= L_ && R_ <= r_) {
      sum[now_] += 1ll * (R_ - L_ + 1) * val_;
      tag[now_] += val_;
      return ;
    }
    Pushdown(now_, L_, R_);
    if (l_ <= mid) Modify(ls, L_, mid, l_, r_, val_);
    if (r_ > mid) Modify(rs, mid + 1, R_, l_, r_, val_);
    Pushup(now_);
  }
  LL Query(int now_, int L_, int R_, int l_, int r_) {
    if (l_ <= L_ && R_ <= r_) return sum[now_];
    Pushdown(now_, L_, R_);
    LL ret = 0;
    if (l_ <= mid) ret += Query(ls, L_, mid, l_, r_);
    if (r_ > mid) ret += Query(rs, mid + 1, R_, l_, r_);
    return ret  ;
  }
  #undef ls
  #undef rs
  #undef mid
}
namespace Cut {
  int fa[kN], son[kN], dep[kN], sz[kN], top[kN];
  int d_num, dfn[kN];
  void Dfs1(int u_, int fa_) {
    fa[u_] = fa_;
    sz[u_] = 1;
    dep[u_] = dep[fa_] + 1;
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i];
      if (v_ == fa_) continue ;
      Dfs1(v_, u_);
      if (sz[v_] > sz[son[u_]]) son[u_] = v_;
      sz[u_] += sz[v_];
    }
  }
  void Dfs2(int u_, int top_) {
    dfn[u_] = ++ d_num;
    a[d_num] = ori[u_];
    top[u_] = top_;
    if (son[u_]) Dfs2(son[u_], top_);
    for (int i = head[u_]; i; i = ne[i]) {
      int v_ = v[i];
      if (v_ == fa[u_] || v_ == son[u_]) continue ;
      Dfs2(v_, v_);
    }
  }
  void Modify(int u_, int v_, int val_) {
    for (; top[u_] != top[v_]; u_ = fa[top[u_]]) {
      if (dep[top[u_]] < dep[top[v_]]) std::swap(u_, v_);
      Seg::Modify(1, 1, n, dfn[top[u_]], dfn[u_], val_);
    }
    if (dep[u_] < dep[v_]) std::swap(u_, v_);
    Seg::Modify(1, 1, n, dfn[v_], dfn[u_], val_);
  }
  LL Query(int u_, int v_) {
    LL ret = 0;
    for (; top[u_] != top[v_]; u_ = fa[top[u_]]) {
      if (dep[top[u_]] < dep[top[v_]]) std::swap(u_, v_);
      ret += Seg::Query(1, 1, n, dfn[top[u_]], dfn[u_]);
    }
    if (dep[u_] < dep[v_]) std::swap(u_, v_);
    ret += Seg::Query(1, 1, n, dfn[v_], dfn[u_]);
    return ret;
  }
  int Child(int u_, int v_) { //求链 v_ -> u_ 上距离 v_ 最近的节点
    while (top[u_] != top[v_]) {
      u_ = top[u_];
      if (fa[u_] == v_) return u_; //u_ 是某条重链顶,v_ 是某重链底
      u_ = fa[u_];
    }
    return son[v_]; //u_, v_ 在同一重链
  }
}
#define dfn Cut::dfn
#define sz Cut::sz
void ModifySubtree(int u_, int val_) {
  if (u_ == root) {
    Seg::Modify(1, 1, n, 1, n, val_);
  } else if (dfn[u_] <= dfn[root] && //root 在 u_ 子树内,通过 dfs 序判断
             dfn[root] + sz[root] - 1 <= dfn[u_] + sz[u_] - 1) {
    int pos_ = Cut::Child(root, u_);
    Seg::Modify(1, 1, n, 1, n, val_); //补集转化
    Seg::Modify(1, 1, n, dfn[pos_], dfn[pos_] + sz[pos_] - 1, -val_);
  } else {
    Seg::Modify(1, 1, n, dfn[u_], dfn[u_] + sz[u_] - 1, val_);
  }
}
LL QuerySubtree(int u_) {
  if (u_ == root) return Seg::Query(1, 1, n, 1, n);
  if (dfn[u_] <= dfn[root] && //root 在 u_ 子树内
      dfn[root] + sz[root] - 1 <= dfn[u_] + sz[u_] - 1) {
    int pos_ = Cut::Child(root, u_);
    return Seg::Query(1, 1, n, 1, n) - 
           Seg::Query(1, 1, n, dfn[pos_], dfn[pos_] + sz[pos_] - 1);
  }
  return Seg::Query(1, 1, n, dfn[u_], dfn[u_] + sz[u_] - 1);
} 
//=============================================================
int main() {
  n = read(), root = 1;
  for (int i = 1; i <= n; ++ i) ori[i] = read();
  for (int i = 2; i <= n; ++ i) {
    int u_ = read(), v_ = i;
    AddEdge(u_, v_);
  }
  Cut::Dfs1(root, 0), Cut::Dfs2(root, root);
  Seg::Build(1, 1, n);
  m = read();
  while (m --) {
    int opt = read();
    if (opt == 1) {
      root = read();
    } else if (opt == 2) {
      int u_ = read(), v_ = read(), val_ = read();
      Cut::Modify(u_, v_, val_);
    } else if (opt == 3) {
      int x_ = read(), val_ = read();
      ModifySubtree(x_, val_);
    } else if (opt == 4) {
      int u_ = read(), v_ = read();
      printf("%lld\n", Cut::Query(u_, v_));
    } else if (opt == 5) {
      int x_ = read();
      printf("%lld\n", QuerySubtree(x_));
    }
  }
  return 0;
}
posted @ 2021-02-20 07:00  Luckyblock  阅读(97)  评论(0编辑  收藏  举报