洛谷P3787 冰精冻西瓜 题解
题意:
给定一棵 $ n $ 个节点的树,边权为 $ w $ ,表示从点 $ u $ 到点 $ v $ 所改变的值的倍数,每个点的点权初始都为 $ 0 $ 。
给定 $ q $ 次操作:
-
增加节点 $ i $ 及其所有子树上节点的值
-
查询节点 $ i $ 的点权
思路:
- 边权均不为 $ 0 $ 的情况:
首先,从根节点开始跑 $ dfs $ ,将整棵树维护成 $ dfs $ 序,并维护每个节点 $ i $ 从其根节点到其本身的边权前缀积 $ pre[i] $ $ ( 根节点的前缀积初始化为 $ 1 $ ) $ 。
对于操作 $ 1 $ ,如果使用 $ for $ 循环遍历 $ dfs $ 序中节点 $ i $ 及其所有子树上节点 $ j $ ,那么节点 $ i $ 的点权 $ val[i] = val[i] + (x / pre[i] * pre[i]) $ ,节点 $ j $ 的点权 $ val[j] = val[j] + (x / pre[i] * pre[j]) $ 。
观察,容易发现,节点 $ i $ 及其所有子树上节点 $ j $ 的边权前缀积乘了一个相同量 $ x / pre[i] $ 。 那么,我们可以使用线段树维护区间 $ [dfn_i, dfn_i + size_i] $ ,令$ val[j] = val[j] + x / pre[i] $ $ \space (dfn_i \space \le \space j \space \le \space dfn_i + size_i) $ 。
对于操作 $ 2 $ , $ val[i] * pre[i] $ 即为最终答案。
- 边权有 $ 0 $ 的情况:
如果一条边 $ u $ -> $ v $ ,边权 $ w = 0 $ ,那么节点 $ v $ 的前缀积 $ pre[v] = 0 $ ,节点 $ v $ 的所有子树上节点 $ j $ 的前缀积 $ pre[j] = 0 $ 。如果增加节点 $ v $ 及其所有子树上节点 $ j $ 的值,节点 $ v $ 及其子树上节点 $ j $ 的前缀积乘了一个相同量 $ x / k_v $ ,由于 $ k_v = 0 $ ,会导致“除 $ 0 $ 错误”。增加节点 $ v $ 的任意一个子树上任意一个节点 $ j $ 及节点 $ j $ 所有子树上节点 $ l $ 的值同理。
因此,我们将所有边权 $ w = 0 $ 的边删掉,将初始的整棵树变为若干棵树,从每棵树的根节点开始跑 $ dfs $ ,将每棵树维护成 $ dfs $ 序,并维护每个节点 $ i $ 从其根节点到其本身的边权前缀积 $ pre[i] $ (每个根节点的前缀积初始化为 $ 1 $ )。
参考“边权均不为 $ 0 $ ”的过程,使用线段树维护即可。
浙公网安备 33010602011771号