P10148 [Ynoi1999] M47升级型“钢铁阿诺” 正常做法

题意

给定长为 \(n\) 的序列,一开始值全为 \(0\),操作是区间推平和查询区间和,询问是从第 \(L\) 个操作执行第 \(R\) 个操作中区间和的和,允许离线。

做法

首先考虑对于每个询问做扫描线,即从左到右将当前操作对操作 \(1\) 到操作 \(R\) 的影响用一些东西表示出来。

因为这种东西感觉不存在低于根号的做法,所以先对序列分块,然后区间和就有散块和整块,于是拆成三种情况分别分析。

  • 所有修改对散块的贡献

对于这部分的贡献,我们考虑枚举区间和的散块,看这个值是什么时候被整块或散块覆盖的,然后把权值(也就是贡献)放到修改这个值的操作上。这样一定是对的,因为当这个地方有值的时候,对它产生影响的一定是最近的覆盖,更遥远的覆盖一定不如最近的那次覆盖。当需要计算贡献的时候,直接把大于等于 \(L\) 的拿出来算和即可。

发现需要支持一个 \(\mathcal O(n \sqrt n)\) 次单点修改,\(\mathcal O(n)\) 次查询后缀的和,使用树状数组就太不平衡了,直接用 \(\mathcal O(1) - \mathcal O(\sqrt n)\) 分块可以完美平衡。

  • 整块修改对整块的贡献。

说明一下,整块修改被打散了不属于这一部分。

然后你会发现因为块的数量只有 \(\mathcal O(\sqrt n)\) 个,那和上一种情况就没有本质区别了,直接做就好。

  • 散块修改对整块的贡献

这里的散块修改包括被打散的整块修改,正常的散块修改和被别的散块修改或整块修改覆盖所以要删除的散块修改。

先考虑贡献的计算,对于新放上去的散块修改,你可以记录当前整块的求和次数为 \(sum_i\),然后你需要放上去的权值为 \(a_i\),那么你需要维护 \(\sum_{i=L}^{R} a_i (sum_R-sum_i)\),发现可以拆成前后两部分贡献分别计算。

对于打散整块修改与覆盖别的散块覆盖,你考虑找到那些值被放上去的时间,然后在那个时间上加上你需要删除或增加的贡献就好了。

发现前两种散块修改数量都是 \(\mathcal O(n)\) 级别的,而且后面那种因为有颜色段均摊,也是 \(\mathcal O(n)\) 级别的。但是查询分了块,数量是 \(\mathcal O(n \sqrt n)\) 的,于是使用两个 \(\mathcal O(\sqrt n) - \mathcal O(1)\) 的分块,分别维护 \(sum_i \times a_i\)\(a_i\) 和需要删除和增加的贡献即可。

posted @ 2025-11-23 10:54  jerry1717  阅读(7)  评论(0)    收藏  举报