1 条题解

  • 0
    @ 2025-10-25 17:58:26

    题目分析

    我们有一棵 NN 个节点的树,每个节点有一个初始高度 HjH_j0Hj<L0 \le H_j < L)。支持两种操作:

    1. 洒水操作:以节点 XX 为中心,半径 DD 内的所有节点高度乘以 WW 后对 LL 取模
    2. 查询操作:询问某个节点的当前高度

    关键限制D40D \le 40,这个限制是解题的突破口。


    核心思路

    1. 问题转化

    直接遍历每个操作影响的所有节点是不可行的(N,QN,Q 太大)。由于 DD 很小,我们可以考虑标记传递的方法。

    核心思想:将一次洒水操作的影响记录在相关节点上,查询时再收集所有影响

    2. 标记设计

    对于每个节点 uu,我们维护一个数组 mul[u][d]mul[u][d]0d2Dmax0 \le d \le 2D_{\max}),表示"对距离节点 uudd 的所有节点施加的乘法因子的累积乘积"。

    为什么数组大小是 2Dmax2D_{\max}

    • 因为一个洒水操作 (X,D,W)(X, D, W) 可能影响 XX 的祖先节点,对于祖先节点 vv,它到 XX 的距离为 hh,那么它需要记录对距离自己为 DhD-h 的节点的影响。
    • 最坏情况下 h=Dh = D,此时 Dh=0D-h = 0h=0h = 0Dh=DD-h = D;同时还要考虑另一个方向的影响。

    3. 洒水操作的处理

    对于洒水操作 (X,D,W)(X, D, W)

    1. XX 开始向上遍历祖先(包括 XX 自己),设当前节点为 vvvvXX 的距离为 hh
    2. 如果 h>Dh > D,停止向上遍历
    3. 剩余半径 r=Dhr = D - h
    4. mul[v][r]mul[v][r] 乘以 WW(模 LL

    这样,我们就把洒水操作的影响记录在了 XX 及其祖先节点上。

    4. 查询操作的处理

    要查询节点 uu 的当前高度:

    1. uu 开始向上遍历祖先(包括 uu 自己),设当前节点为 vvvvuu 的距离为 hh
    2. 如果 h>Dmaxh > D_{\max},停止向上遍历
    3. mul[v][h]mul[v][h] 乘到结果上(模 LL

    为什么这样是正确的?

    • 对于任意洒水操作 (X,D,W)(X, D, W),如果它影响了节点 uu,那么 uuXX 的距离 dist(u,X)Ddist(u,X) \le D
    • 在洒水时,我们在 XX 的某个祖先 vv 处记录了影响,且 dist(v,X)+dist(v,u)=dist(X,u)Ddist(v,X) + dist(v,u) = dist(X,u) \le D
    • 查询时,uu 会访问到 vv,且 dist(v,u)dist(v,u) 正好对应洒水时记录的位置

    5. 时间复杂度分析

    • 每次洒水操作:向上遍历至多 DD 个节点,O(D)O(D)
    • 每次查询操作:向上遍历至多 DD 个节点,O(D)O(D)
    • 总时间复杂度:O((N+Q)D)O((N+Q)D),在 D40D \le 40 时非常高效

    算法优势

    1. 利用了小半径特性D40D \le 40 使得我们可以为每个节点存储大小为 O(D)O(D) 的数组
    2. 标记永久化:不需要下推标记,简化了实现
    3. 树上差分思想:将区间修改转化为多个点的标记更新
    4. 离线处理:算法本质是在线算法,但思想类似于离线差分

    总结

    这道题的解决关键在于:

    • 发现 DD 很小的特殊性质
    • 设计合适的标记结构 mul[u][d]mul[u][d]
    • 利用树的父子关系实现高效的标记更新和收集
    • 通过"洒水时分散标记,查询时收集标记"的方式避免直接遍历影响区域

    这种"小半径操作 + 标记永久化"的思路在树上操作问题中非常有用,特别是当操作半径有上限时。

    • 1

    信息

    ID
    4080
    时间
    4000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    6
    已通过
    1
    上传者