树上差分
给一条链(u,v)加1,在u处+1,v处+1,LCA处-2。
用树状数组在dfs序上单点修改单点查询,但是修改是往前贡献,查询是往后查询,
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
如果询问都在修改之后可以直接树形DP由下往上传,
O
(
n
)
O(n)
O(n)。
eg:BZOJ3631: [JLOI2014]松鼠的新家
查询一个点到根的路径的和
有时候链上问题可以转化成两点和lca到根的路径和问题。
如果是单点修改,用树状数组在dfs序上对子树差分,单点查询(即前缀和),
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
eg:BZOJ2819: Nim
如果还有子树修改,考虑修改一个点
x
x
x对它的子树中点
y
y
y的贡献:
- 单点修改 x x x,加上 a a a,贡献为 a a a
- 子树修改 x x x,子树加上 a a a,贡献为 a ∗ ( d e e p [ y ] − d e e p [ x ] + 1 ) = a ∗ d e e p [ y ] + a ∗ ( 1 − d e e p [ x ] ) a*(deep[y]-deep[x]+1)=a*deep[y]+a*(1-deep[x]) a∗(deep[y]−deep[x]+1)=a∗deep[y]+a∗(1−deep[x])
维护两个树状数组,一个存直接贡献(单点修改的a和子树修改的a*(1-deep[x])),一个存间接贡献(子树修改的a,求后乘上对应的deep)即可。
eg:BZOJ4034: [HAOI2015]树上操作
万能解法:树链剖分+线段树
无论是求和、异或、最大值、最小值、颜色段数、单点修改、子树修改、链修改统统一套搞定。
对一个点所有儿子子树做不同修改:
只对重儿子做修改,轻儿子就在查询时往上跳链,加上链顶的父亲未做的修改贡献。见CF1254D Tree Queries
链并
求几个点到根的链并可以将点按dfs序排序后每个点+1,相邻两点LCA-1。
求几条树链的并可以把每条链按树链剖分取dfs序区间,然后求区间并。