轻重链剖分

轻重链剖分可以支持 \(O(n \log n)\) 预处理,\(O(\log^2n)\) 修改/查询树的一条链的信息,\(O(\log n)\) 修改/查询一个子树的信息。
由于基于线段树维护被剖开的链,轻重链剖分维护的信息必须是能用线段树来合并的。

轻重链剖分划分链的原则:
  1. 对于一个非叶子节点,子树大小最大的儿子被称为重儿子。
  2. 重边为所有非叶子节点与其重儿子的连边,其余的边为轻边。
  3. 重边连成的链为重链。

那么对于一次链上的询问,由于不想在加入求 LCA 的步骤,所以在 \(x,y\) 不在一条重链上时,选择重链顶端更深的节点,维护这条链上的信息,并把节点跳到链顶端的父亲节点。
这和倍增求 LCA 的做法相似,只是把一步走 \(2^k\) 变成了走到链顶端的父亲。

while(top[x]!=top[y]){
    if(dep[top[x]]<dep[top[y]])swap(x,y);
    //此处为修改/查询操作 
    x=fa[top[x]];
}
//此处为处理最后一条链

\(top_x\) 表示 \(x\) 所在重链的顶端)
考虑如何快速修改,查询:
注意到,当每次重链上的编号连续时,可以用线段树维护。
所以先 dfs 一遍,优先走每个节点的重儿子,记这个时候的 dfs序为新的编号。
此时一颗子树内的编号也一定连续,就都能用线段树维护了。

复杂度分析:

性质1:如果存在边 \(u,v\)\(v\) 不是 \(u\) 的重儿子,那么 \(2*siz_v\leq siz_u\)。(\(siz_x\) 表示 \(x\) 的子树大小)
性质2:根节点到任意节点的路径上,经过的轻边和重链的个数都不超过 \(\log n\) 个。
考虑证明性质2,由性质1,每走一条轻边,子树大小减少一半,所以轻边不超过 \(\log n\) 个,由于重链一定是极大连续的,所以重链也不超过 \(\log n\) 个。
由性质2,一次操作的复杂度为 \(O(\log^2 n )\)

P3384 【模板】轻重链剖分/树链剖分
模板,线段树维护区间加,区间和。

P2486 [SDOI2011]染色
模板,线段树维护左右端点颜色,区间有多少颜色段,一个标记维护区间修改。
链之间信息的合并与线段树信息合并类似,要记录当前链顶端的颜色,合并时判断。

P4211 [LNOI2014]LCA
先转化题意,节点的深度可以理解为从根节点到节点的链上区间加1,然后求和。
易想到莫队,但这样的复杂度为 \(O(n*\sqrt n*\log^2n)\)
我们发现区间信息只维护一个和,也就是说可以 \(O(1)\) 加减,即:

\[\sum_{i=l}^r dep_{lca(i,z)}=\sum_{i=1}^r dep_{lca(i,z)}-\sum_{i=1}^l dep_{lca(i,z)} \]

所以左端点一定,就无需莫队,将询问离线,然后从小到大加入节点,遇到询问有关的就查询更新。

P5305 [GXOI/GZOI2019]旧词
上道题的加强版,考虑 \({dep_x}^k\) 的意义。
上道题能转化为区间加以是因为 \(dep_{fa_x}+1=dep_x-1+1=dep_x\),所以对于这道题,应该加上 \({(dep_x+1)}^k-{dep_x}^k\)
也就是说,要维护 \(val_x*a_x\) ,支持区间 \(a\) 数组加1和区间查。
用线段树维护即可。

posted @ 2021-11-12 09:38  字如其人  阅读(111)  评论(0)    收藏  举报