1 条题解

  • 0
    @ 2025-12-8 18:30:13

    「三重求和」题解

    问题分析

    给定序列 a1,,ana_1, \dots, a_n,对于每个询问 (d,p1,p2)(d, p_1, p_2),计算:

    $$S = \sum_{i=0}^{d-1} \sum_{j=0}^{d-1} \sum_{k=0}^{d-1} a_{p_1+d\cdot i+j} \cdot a_{p_2 + d\cdot j + k} $$

    其中 0i,j,k<d0 \leq i,j,k < d,需要保证下标在 [1,n][1, n] 范围内。

    表达式分析

    1. 下标分析

    设:

    • X=p1+di+jX = p_1 + d\cdot i + j
    • Y=p2+dj+kY = p_2 + d\cdot j + k

    其中:

    • i,j,ki,j,k 各自独立地从 00d1d-1
    • XX 依赖于 iijj
    • YY 依赖于 jjkk

    2. 重写求和式

    $$S = \sum_{j=0}^{d-1} \left( \sum_{i=0}^{d-1} a_{p_1+d\cdot i+j} \right) \cdot \left( \sum_{k=0}^{d-1} a_{p_2 + d\cdot j + k} \right) $$

    令:

    • Aj=i=0d1ap1+di+jA_j = \sum_{i=0}^{d-1} a_{p_1+d\cdot i+j},其中 0j<d0 \leq j < d
    • Bj=k=0d1ap2+dj+kB_j = \sum_{k=0}^{d-1} a_{p_2 + d\cdot j + k},其中 0j<d0 \leq j < d

    则:

    S=j=0d1AjBjS = \sum_{j=0}^{d-1} A_j \cdot B_j

    3. 几何解释

    AjA_j 是从位置 p1+jp_1+j 开始,步长为 dddd 个元素之和。 BjB_j 是从位置 p2+djp_2+d\cdot j 开始,连续的 dd 个元素之和。

    具体来说:

    • AjA_j:在序列 aa 中,取位置 p1+j,p1+j+d,p1+j+2d,,p1+j+(d1)dp_1+j, p_1+j+d, p_1+j+2d, \dots, p_1+j+(d-1)ddd 个元素求和
    • BjB_j:在序列 aa 中,取位置 $p_2+d\cdot j, p_2+d\cdot j+1, p_2+d\cdot j+2, \dots, p_2+d\cdot j+(d-1)$ 共 dd 个元素求和

    算法思路

    暴力方法(不可行)

    直接按照公式计算:

    • 对每个询问,计算 AjA_jBjB_j 各需要 O(d2)O(d^2) 时间(因为每个 AjA_j 需要求 dd 个元素的和)
    • 总复杂度 O(md2)O(m \cdot d^2),最坏 O(mn2)O(m \cdot n^2),不可行

    优化方向

    我们需要预处理一些信息,使得能够快速计算 AjA_jBjB_j

    优化 AjA_j 的计算

    Aj=i=0d1ap1+di+jA_j = \sum_{i=0}^{d-1} a_{p_1+d\cdot i+j}

    对于固定的 ddp1p_1AjA_jjj 的函数。这可以看作在模 dd 意义下的分组求和。

    定义序列 bt=ap1+tb_t = a_{p_1+t}(重新编号,从0开始),则:

    $$A_j = \sum_{i=0}^{d-1} b_{j + d\cdot i} = \sum_{i: i \equiv j \ (\text{mod } d)} b_i $$

    AjA_jbb 序列中所有模 ddjj 的位置的和。

    优化 BjB_j 的计算

    Bj=k=0d1ap2+dj+kB_j = \sum_{k=0}^{d-1} a_{p_2 + d\cdot j + k}

    这是连续的 dd 个元素的和,可以使用前缀和优化:

    pre[i]=t=1iatpre[i] = \sum_{t=1}^i a_t,则:

    $$B_j = pre[p_2 + d\cdot j + d - 1] - pre[p_2 + d\cdot j - 1] $$

    前提是下标在范围内。

    预处理策略

    AjA_j 的预处理

    对于每个可能的模数 dd,我们需要快速查询:从位置 p1p_1 开始,所有模 ddjj 的位置的和。

    f(d,r,start,len)f(d, r, start, len) 表示从位置 startstart 开始,长度为 lenlen 的范围内,所有模 ddrr 的位置的元素和。

    但由于 dd 最多可达 nn,无法为所有 dd 预处理。

    根号分治

    使用根号分治策略:

    • 对于小的 dddnd \leq \sqrt{n}):预处理所有可能的信息
    • 对于大的 ddd>nd > \sqrt{n}):直接暴力计算,因为此时循环次数 dd 较小

    设阈值 B=n450B = \sqrt{n} \approx 450(因为 n2×105n \leq 2\times 10^5)。

    dd 的情况(dBd \leq B

    预处理数组 sum[d][r][t]sum[d][r][t] 表示:前 tt 个位置中,所有模 ddrr 的元素之和。

    那么:

    $$A_j = sum[d][j][p_1 + (d-1)d + j] - sum[d][j][p_1 - 1] $$

    但注意:我们只需要从 p1p_1 开始,每隔 dd 取一个元素,共取 dd 个。

    更精确地说:AjA_j 是从 p1+jp_1+j 开始,每隔 dd 取一个,取 dd 次。

    可以预处理 g[d][r][k]g[d][r][k] 表示从位置 rr 开始,每隔 dd 取一个,取 kk 个的和。但这样空间太大。

    更好的方法:对于每个小 dd,我们预处理一个前缀和数组 pred[i]pre_{d}[i],表示前 ii 个位置中,所有模 dd00 的元素之和,余 11 的元素之和,...,余 d1d-1 的元素之和。

    dd 最多 BB,每个 dd 需要 nn 个位置,每个位置存储 dd 个余数的和,总空间 O(B2n)O(B^2 \cdot n),太大。

    实际上,我们可以只存储每个余数的前缀和:sum[d][r][i]sum[d][r][i] 表示前 ii 个位置中模 ddrr 的元素之和。

    空间:$O(\sum_{d=1}^B d \cdot n) = O(B^2 \cdot n) = O(n^{1.5} \cdot n) = O(n^{2.5})$,还是太大。

    需要进一步优化。

    改进的小 dd 预处理

    注意到 AjA_j 需要的是从特定位置开始,每隔 dd 取一个的和,而不是所有模 ddjj 的和。

    我们可以预处理另一种结构:对于每个 dd,将序列分成 dd 个等差数列,然后对每个等差数列预处理前缀和。

    具体来说:

    • 对于模 ddrr 的位置:r,r+d,r+2d,r, r+d, r+2d, \dots
    • 对这些位置建立前缀和数组 prefix[d][r][k]prefix[d][r][k],表示前 kk 个这样的元素的和

    那么 AjA_j 就是从 p1+jp_1+j 开始(这是第 tt 个这样的元素),取 dd 个连续元素的和。

    空间:每个 dddd 个等差数列,每个数列长度约 n/dn/d,总空间 $O(\sum_{d=1}^B d \cdot (n/d)) = O(B \cdot n) = O(n^{1.5})$,可接受。

    dd 的情况(d>Bd > B

    此时 dd 较大,但 dd 的上限是多少?由于 p1+d(d1)+(d1)np_1 + d\cdot (d-1) + (d-1) \leq n,所以 d2nd^2 \leq n,即 dnd \leq \sqrt{n}

    等等,检查一下:X=p1+di+jX = p_1 + d\cdot i + j 最大是 p1+d(d1)+(d1)=p1+d21np_1 + d\cdot (d-1) + (d-1) = p_1 + d^2 - 1 \leq n,所以确实 d2nd^2 \leq n

    因此 dn447d \leq \sqrt{n} \approx 447,这与我们的阈值 BB 相同!

    这意味着所有询问的 dd 都满足 dnd \leq \sqrt{n}

    重要发现:由于下标限制 p1+d21np_1 + d^2 - 1 \leq n,所以 dnd \leq \sqrt{n}

    因此所有询问的 dd 都是"小"的,我们可以直接使用小 dd 的预处理方法。

    最终算法

    预处理

    B=nB = \lfloor \sqrt{n} \rfloor

    对于每个 d=1d = 1BB

    1. 将序列分成 dd 个等差数列:对于 r=0r = 0d1d-1,位置为 r,r+d,r+2d,r, r+d, r+2d, \dots
    2. 对每个等差数列建立前缀和数组 pre[d][r][k]pre[d][r][k],其中 pre[d][r][k]pre[d][r][k] 表示该等差数列前 kk 个元素的和

    具体实现:对于每个 dd,我们有 dd 个数组,每个数组长度约为 n/dn/d

    查询计算

    对于询问 (d,p1,p2)(d, p_1, p_2)

    1. 计算 AjA_j0j<d0 \leq j < d

    对于每个 jjAjA_j 是等差数列 j,j+d,j+2d,j, j+d, j+2d, \dots 中从某个位置开始的连续 dd 个元素的和。

    从哪个位置开始?我们需要找到 p1+jp_1+j 在这个等差数列中是第几个元素。

    设等差数列为:j,j+d,j+2d,j, j+d, j+2d, \dots 元素 p1+jp_1+j 在这个数列中的索引为:t=(p1+j)jd=p1dt = \frac{(p_1+j) - j}{d} = \frac{p_1}{d}?不对,p1+jp_1+j 可能不是等差数列的起点。

    实际上,等差数列的第一个元素是某个 rr 满足 rj (mod d)r \equiv j \ (\text{mod } d),且 rjr \leq j。更简单的方法:

    等差数列中的第 kk 个元素是 j+(k1)dj + (k-1)d

    我们需要找到最小的 kk 使得 j+(k1)dp1+jj + (k-1)d \geq p_1+j,即 (k1)dp1(k-1)d \geq p_1,所以 k=p1/d+1k = \lceil p_1/d \rceil + 1

    实际上更简单:我们要求的是从 p1+jp_1+j 开始(包含),每隔 dd 取一个,取 dd 个。

    start=p1+jstart = p_1+j,那么取的序列是:start,start+d,start+2d,,start+(d1)dstart, start+d, start+2d, \dots, start+(d-1)d

    这些元素在等差数列 j,j+d,j+2d,j, j+d, j+2d, \dots 中的索引:

    第一个元素 startstart 在等差数列中的索引:$t_1 = \frac{start - j}{d} = \frac{p_1+j - j}{d} = \frac{p_1}{d}$

    最后一个元素 start+(d1)dstart+(d-1)d 的索引:$t_2 = \frac{p_1+j+(d-1)d - j}{d} = \frac{p_1}{d} + (d-1)$

    所以 Aj=pre[d][j][t2+1]pre[d][j][t1]A_j = pre[d][j][t_2+1] - pre[d][j][t_1],使用前缀和计算。

    2. 计算 BjB_j0j<d0 \leq j < d

    Bj=k=0d1ap2+dj+kB_j = \sum_{k=0}^{d-1} a_{p_2 + d\cdot j + k}

    这是连续 dd 个元素的和,使用普通的前缀和:

    $$B_j = pre\_global[p_2 + d\cdot j + d - 1] - pre\_global[p_2 + d\cdot j - 1] $$

    其中 pre_global[i]=t=1iatpre\_global[i] = \sum_{t=1}^i a_t 是全局前缀和。

    3. 计算答案

    S=j=0d1AjBjS = \sum_{j=0}^{d-1} A_j \cdot B_j

    需要模 2322^{32},可以使用 unsigned int 自动取模。

    复杂度分析

    预处理

    • 对于每个 dd,需要处理 dd 个等差数列
    • 每个等差数列平均长度 n/dn/d
    • 总元素数:$\sum_{d=1}^B d \cdot (n/d) = B \cdot n = O(n^{1.5})$
    • 预处理复杂度:O(n1.5)O(n^{1.5})

    查询

    • 对于每个询问,需要计算 ddAjA_jddBjB_j
    • 每个 AjA_j 需要 O(1)O(1) 时间(通过预处理的等差数列前缀和)
    • 每个 BjB_j 需要 O(1)O(1) 时间(通过全局前缀和)
    • 计算点积需要 O(d)O(d) 时间
    • 总查询复杂度:O(i=1mdi)O(\sum_{i=1}^m d_i)

    由于 dind_i \leq \sqrt{n},总复杂度 O(mn)O(m\sqrt{n}),对于 m=2×105m=2\times 10^5,最坏 2×105×4479×1072\times 10^5 \times 447 \approx 9\times 10^7,可接受。

    实现细节

    1. 数据结构

    • pre_global[i]:全局前缀和
    • 对于小 dd 的预处理:使用二维向量或数组存储 pre[d][r][k]
      • 第一维:dd(1到BB
      • 第二维:余数 rr(0到d1d-1
      • 第三维:前缀和索引

    2. 内存优化

    由于 B447B \approx 447dd 的最大值是447。 对于每个 dd,需要存储 dd 个数组。 总元素数大约 $447 \times n = 447 \times 2\times 10^5 \approx 9\times 10^7$ 个整数。 每个整数4字节,约360MB,可能太大。

    需要进一步优化内存:

    优化方案

    1. 不存储所有 dd 的预处理信息,只存储 dd 较小的部分
    2. 或者,对于每个 dd,我们不需要存储所有余数的前缀和,只需要在查询时临时计算

    实际上,计算 AjA_j 也可以通过另一种方式:AjA_j 就是从 p1+jp_1+j 开始,每隔 dd 取一个,取 dd 个。我们可以直接计算这 dd 个元素的和,每个元素用全局前缀和快速获取。

    3. 简化实现

    由于 dnd \leq \sqrt{n},我们可以直接暴力计算 AjA_j

    对于每个 jj

    Aj=i=0d1ap1+di+jA_j = \sum_{i=0}^{d-1} a_{p_1+d\cdot i+j}

    这需要 O(d)O(d) 时间计算一个 AjA_j,总共 O(d2)O(d^2) 时间计算所有 AjA_j

    d2nd^2 \leq n,所以 O(d2)O(n)O(d^2) \leq O(n)

    对于 BjB_j,使用全局前缀和 O(1)O(1) 计算。

    总查询复杂度:O(mn)O(m \cdot n)?不对,是 O(d2)O(\sum d^2)

    最坏情况:m=2×105m=2\times 10^5,每个 d=n447d=\sqrt{n}\approx447d22×105d^2\approx2\times 10^5,总 2×105×2×105=4×10102\times 10^5 \times 2\times 10^5 = 4\times 10^{10},太大。

    4. 折中方案

    使用阈值 TT

    • dTd \leq T 时,使用预处理方法
    • d>Td > T 时,直接暴力计算 AjA_j(此时 dd 较小,d2d^2 也较小)

    T=n2/33400T = n^{2/3} \approx 3400(对于 n=2×105n=2\times 10^5),但 dd 最大只有447,所以所有 dd 都小于 TT

    总结

    最终算法:

    1. 预处理全局前缀和
    2. 对于 d=1d=1n\sqrt{n},预处理每个等差数列的前缀和
    3. 对于每个询问:
      • O(1)O(1) 时间计算每个 BjB_j(全局前缀和)
      • O(1)O(1) 时间计算每个 AjA_j(等差数列前缀和)
      • 计算点积 S=j=0d1AjBjS = \sum_{j=0}^{d-1} A_j \cdot B_j

    复杂度:

    • 预处理:O(n1.5)O(n^{1.5})
    • 查询:O(mn)O(m \cdot \sqrt{n})

    对于 n=2×105n=2\times 10^5n1.59×107n^{1.5} \approx 9\times 10^7mn9×107m\sqrt{n} \approx 9\times 10^7,均可接受。

    关键优化是发现 dnd \leq \sqrt{n} 的约束,从而可以使用根号分治策略。

    • 1

    信息

    ID
    5905
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者