这个题 800 吧。
- 给你一棵 n 个点的有根树,根为 1,边带权。每个点有点权 ai,初始 ai=0。记 dis(x,y) 表示 x,y 简单路径上的边权和。
- q 次操作:
1 x y:对于 x 子树内的点 u,au←au+dis(u,y)。
2 x:查询 x 子树内的点的点权和。
- n,q≤5×105。时限 4 秒。
省流:
记
fu 为
u 的父亲,
wu 为
u→fu 这条边的权值,不妨
f1=0;
sx 为以
x 为根的子树大小。直接考虑每个点的点权变化没那么容易,但是注意到在一个修改操作中,
fu 和
u 点权变化量总是差一个
wu 或
−wu。这启示我们将点权做差分。
令
di=au−afu,记
u 根链上的点依次是
p1=1,…,pk=u。那么
au=i=1∑kdpi。
先考虑比较简单的情形,即
y 在
x 子树外。此时
dx 增加
dis(x,y)。对于
x 子树内的其他点
u,
du 增加
wu。
再考虑
y 在
x 子树内的情况,也就是省流的那张图。我们发现不在
x⇝y 路径上的点
u,
du 仍然增加
wu。而在路径上的点(不包括
x),
du 会减少
wu。
考虑我们要查询什么,即子树内所有点的根链
d 值和的和。考虑每个点
u 的
du 会对哪些点产生贡献。若
u 在
1⇝x 上,那么会对整棵子树的点都产生贡献,为
dusx。若其在
x 子树内,它会对以
u 为根的子树内的点产生贡献,为
dusu。
现在问题来链、子树
du 加
kwu+b,链查
∑du,子树查
∑dusu。重链剖分,问题变为区间
du 加
kwu+b,区间查
∑du 和
∑dusu。使用你喜欢的数据结构维护即可。我是用的是树状数组。
怎么维护
相当于给出三个数列
di,si,wi,其中
wi,si 为常数列。
di 要支持的操作为区间加
Awi+B,求区间
∑disi。
∑du 就是的
su=1 的情况。
直接维护
disi 前缀和
DSi。考虑一个操作对前缀和序列造成哪些影响。假设操作区间为
[l,r]。
记
Si 为
si 的前缀和,
WSi 为
wisi 的前缀和。
对于
i∈[r+1,n] 这部分,
Pi 的增量为
dlsl∼drsr 的增量之和,为
B(Sr−Sl−1)+A(WSr−WSl−1)。
对于
i∈[l,r] 这部分,
Pi 的增量为
dlsl∼disi 的增量之和,为
B(Si−Sl−1)+A(WSi−WSl−1)。
我们维护
Pi=AiWSi+BiSi+Ci,则操作可以表示为
Ai,Bi,Ci 的区间加。由于维护的是前缀和数组,求原数组的区间和只需要单点查。所以只需要区间加、单点查。就可以树状数组维护了。
认为
n,q 同阶,时间复杂度
O(qlog2n),空间复杂度
O(n)。常数比线段树小,跑到了最优解。