[BJOI2018] 链上二次求和 题解

题目链接

原问题没法直接维护。先推式子,看看能得到什么。

\(f(l, r)\) 表示给定 \(l, r\) 时的答案。那么根据定义,有:

\[f(l, r) = \sum_{len = l}^{r} \sum_{i = 1}^{n - len + 1}\sum_{j = i}^{i + len - 1} a_j \]

\(s(m) = \sum_{i = 1}^{m} a_i\),则我们可以去掉第三个求和符号:

\[f(l, r) = \sum_{len = l}^{r} \sum_{i = 1}^{n - len + 1} [s(i + len - 1) - s(i - 1)] \]

\(ss(m) = \sum_{i = 1}^{m} s(i)\),继续推导,去掉第二个求和符号:

\[f(l, r) = \sum_{len = l}^{r} [ss(n) - ss(len - 1) + ss(n - len)] \]

\(sss(m) = \sum_{i = 1}^{m} ss(i)\),继续推导,去掉最后的求和符号:

(为了应对边界条件,定义当 \(m \le 0\) 时,\(ss(m) = sss(m) = 0\),这是自然的。)

\[\boxed{f(l, r) = (r - l + 1)ss(n) + sss(r - 1) - sss(l - 2) + sss(n - l) - sss(n - r - 1)} \]

以上,我们就去掉了所有的求和符号,这是进一步优化算法的基础。实际上,有了这个式子之后,我们就可以做到 \(O(1)\) 查询。但修改时,我们必须重新求出 \(s, ss, sss\) 数组,这样做的时间复杂度为 \(O(n)\)

想想怎么优化。看到题目中的区间操作,很自然地想到用线段树维护。那么,维护什么以及如何维护呢?

首先明确我们的需求:区间修改,快速计算 \(ss(m), sss(m)\)。从这里以后有两条路:

  1. 预处理出原序列 \(a\)\(ss\) 数组。修改时,考察 \(a\) 的区间加对 \(ss\) 产生的影响,用线段树维护 \(ss\) 的区间和。维护 \(ss\) 的好处在于:\(sss\)\(ss\) 的前缀和,因此要求 \(sss\) 时,只要在线段树上查询 \(ss\) 的区间和即可。

  2. 维护 \(a\) 和一些辅助数组,把 \(ss\)\(sss\) 都写成用 \(a\) 表示的形式。

    注意 \(ss\)\(sss\) 是不能作为区间信息维护的。实际上,带“前缀”的信息都不能作为区间信息维护。以更简单的一阶前缀和 \(s\) 为例,考虑这样的问题:区间加,求任意位置的前缀和。在线段树中,对于一个区间 \([l, r]\),我们不能维护区间 \([l, r]\) 的前缀和 \(s\),因为这不能合并:\([l, mid]\) 的前缀和与 \([mid + 1, r]\) 的前缀和相加,不能得到 \([l, r]\) 的前缀和。我们只能维护 \([l, r]\) 的区间和,而这个信息是可以合并的。求某个位置 \(p\) 的前缀和,看作 \([1, p]\) 的区间和即可。

    如果选择这种方式,要继续推式子。

下面选用第二种方法。继续推导:

\[\begin{aligned} ss(m) &= \sum_{i = 1}^{m} s(i) \\ &= \sum_{i = 1}^{m}\sum_{j = 1}^{i}a_j \\ &= \sum_{i = 1}^{m} (m - i + 1)a_i \\ &= (m + 1)s(m) - \sum_{i = 1}^{m} ia_i \end{aligned} \]

这里出现了新的求和式:\(\sum_{i = 1}^{m}ia_i\),我们记为 \(si(m)\),则

\[\boxed{ss(m) = (m + 1)s(m) - si(m)} \]

这样我们就把 \(ss\)\(s\)\(si\) 表示了出来。显然,\(s(m)\) 是容易用 \(a\) 表示的:对 \(a\) 求区间和即可。而 \(si\) 也是可以维护的——它不是某种“前缀”和的形式,因此可以在线段树上合并信息。至于如何维护,等会再说。

下面推导 \(sss\) 的式子:

\[\begin{aligned} sss(m) &= \sum_{i = 1}^{m} ss(i) \\ &= \sum_{i = 1}^{m} [(i + 1)s(i) - si(i)] \\ &= ss(m) + \sum_{i = 1}^{m}is(i) - \sum_{i = 1}^{m} si(i) \end{aligned} \]

\(\sum_{i = 1}^{m}is(i)\)\(\sum_{i = 1}^{m} si(i)\) 都不能直接维护,继续推导:

\[\begin{aligned} \sum_{i = 1}^{m}is(i) &= \sum_{i = 1}^{m} (i\sum_{j = 1}^{i}a_j) \\ &= \sum_{i = 1}^{m} (a_i \sum_{j = i}^{m} j) \\ &= \sum_{i = 1}^{m} \frac{1}{2}(m + i)(m - i + 1)a_i \\ &= \frac{1}{2}\sum_{i = 1}^{m} (m^2 - m + i^2 + i)a_i \\ &= \frac{1}{2}m(m - 1)s(m) + \frac{1}{2}si(m) + \frac{1}{2}\sum_{i = 1}^{m}i^2a_i \end{aligned} \]

(推到第二个等号的要点在于计算每个 \(a_i\) 前的系数:这里,每个 \(a_i\)\(i\) 开始计算,系数为 \(i\),然后为 \(i + 1\)\(i + 2\),一直到 \(m\),因此总系数为 \(\sum_{j = i}^{m} j\)。)

\(sii(m) = \sum_{i = 1}^{m}i^2a_i\),和 \(si\) 类似,这也是可以合并的信息,因此可以用线段树维护。那么

\[\sum_{i = 1}^{m}is(i) = \frac{1}{2}m(m - 1)s(m) + \frac{1}{2}si(m) + \frac{1}{2}sii(m) \]

接着来推导 \(sss\) 的式子中剩下的另一个和式:\(\sum_{i = 1}^{m} si(i)\)

\[\begin{aligned} \sum_{i = 1}^{m} si(i) &= \sum_{i = 1}^{m} \sum_{j = 1}^{i}ja_j \\ &= \sum_{i = 1}^{m} (m - i + 1)ia_i \\ &= (m + 1)si(m) - sii(m) \end{aligned} \]

(这里推导的要点在于把 \(ia_i\) 看作一个整体,然后推导方式和 \(ss\) 相同,只是把求和对象从 \(a_i\) 换成了 \(ia_i\)。)

至此,我们终于可以写出 \(sss\) 的式子:

\[\boxed{sss(m) = \frac{1}{2}m(m + 1)s(m) + ss(m) - (m + \frac{1}{2})si(m) + \frac{1}{2}sii(m)} \]

\(ss\)\(sss\) 的式子可以看出,我们需要维护的量有 \(s, si\)\(sii\)。最后要考虑的是怎么维护区间加对这些量的影响。

考虑一个区间 \([l, r]\),设它的三个量为 \(s, si\)\(sii\)(定义同上文)。设区间加 \(d\) 后,新的量为 \(s', si'\)\(sii'\)。那么

\[s' = \sum_{i = l}^{r} (a_i + d) = s + d\sum_{i = l}^{r}1 \\ si' = \sum_{i = l}^{r} i(a_i + d) = si + d\sum_{i = l}^{r}i \\ sii' = \sum_{i = l}^{r} i^2(a_i + d) = sii + d\sum_{i = l}^{r}i^2 \]

对于 \(\sum_{i = l}^{r}1, \sum_{i = l}^{r}i\)\(\sum_{i = l}^{r}i^2\),可以用公式求,也可以预处理后查表。这显然是好维护的。

回顾一下我们要做什么:

  1. 在线段树上维护 \(s, si\)\(sii\)
  2. 对于每次询问,根据公式求出答案。公式中要用到 \(ss\)\(sss\),这些也要代入公式求,但总归能化到我们维护的三个量。

每次询问时,我们只会在线段树上查询常数次,所以总时间复杂度为 \(O(n + m \log n)\)

综上所述,我们就解决了这个问题。

AC 记录

posted @ 2025-01-19 10:41  DengStar  阅读(40)  评论(0)    收藏  举报