G. Modular Sorting

Codeforces 第 1034 轮 (Div. 3) G

问题

给定一个整数 mm (2m51052 \leq m \leq 5 \cdot 10^5) 和一个由严格小于 mm 的非负整数组成的数组 aa

你需要处理两种类型的查询:

  1. i x: 赋值 ai:=xa_i := x (此操作会持久更新数组)。
  2. k: 在一次操作中,你可以选择任意元素 aia_i 并赋值 ai:=(ai+k)modma_i := (a_i + k) \bmod m。判断是否存在一个这样的操作序列(可能为零次),可以使数组变为 非递减 的(即 a0a1an1a_0 \leq a_1 \leq \dots \leq a_{n-1})。

注意:类型 2 的查询是 假设性 的 — 它们不会修改数组。类型 1 的查询是 持久性 的,会永久改变数组。


题解

基本思想

对于一个参数为 kk 的类型 2 查询,我们的目标是判断是否存在一组非负整数 t0,t1,,tn1t_0, t_1, \dots, t_{n-1} 使得:

ai=(ai+tik)modm a_i' = (a_i + t_i \cdot k) \bmod m

并且得到的数组 aa' 是非递减的。


模算术与循环子群

g=gcd(k,m)g = \gcd(k, m)。当我们重复应用操作 ai:=(ai+k)modma_i := (a_i + k) \bmod m 时,aia_i 可以达到的值都位于同一个 gg 的陪集 中:

aiai(modg) a_i' \equiv a_i \pmod{g}

这意味着 每个元素在任何次数的以 kk 为步长的操作下,其模 gg 的余数保持不变

因此,任意两个元素 aia_iaja_j 之间的最小差值为 (ajai)modg(a_j - a_i) \bmod g


通过模差分判断可行性

那么,aia_i 的所有可达值构成了这个长度的循环群。为了检查我们是否能将值重新排列成非递减顺序,我们使用一个关键指标,该指标衡量从左到右需要多少模“增量”。

我们定义:

S(g)=j=0n1Δj S(g) = \sum_{j=0}^{n-1} \Delta_j

其中

  • Δ0=a0modg\Delta_0 = a_0 \bmod g
  • Δj=((ajaj1)modg+g)modg\Delta_j = ((a_j - a_{j-1}) \bmod g + g) \bmod g for j1j \geq 1

这个表达式捕捉了在模 gg 下从 aj1a_{j-1} 移动到 aja_j 所需的“正模步长”。

可行性条件:

对于一个给定 kk 的类型 2 查询,令 g=gcd(k,m)g = \gcd(k, m)。那么:

当且仅当 S(g)<mS(g) < m 时,数组可以变为非递减。

如果 S(g)mS(g) \geq m,所需的模位移将会环绕 mm — 违反了非递减约束。


类型 1 查询的高效更新

当处理一个类型 1 查询 (i x) 时,我们需要为 mm所有因子 gg 更新指标 S(g)S(g),因为任何未来的查询都可能使用一个 kk 使得 gcd(k,m)=g\gcd(k, m) = g

aia_i 更改为 xx 最多影响 S(g)S(g) 中的两项:

  • 如果 i>0i > 0,差值 (aiai1)modg(a_i - a_{i-1}) \bmod g
  • 如果 i+1<ni + 1 < n,差值 (ai+1ai)modg(a_{i+1} - a_i) \bmod g

对于每个这样的 gg,我们:

  1. 减去对 S(g)S(g) 的旧贡献
  2. aia_i 更新为 xx
  3. 加回新的贡献

为了保持效率,我们:

  • 预计算 mm 的所有因子
  • 为每个 gg 存储 S(g)S(g)
  • 使用直接访问仅更新受影响的项

总结

  • 每个值 aia_i 只能在其模 gcd(k,m)\gcd(k, m) 的同余类内移动。
  • 为了确定类型 2 查询的可行性,我们使用模步长和 S(g)S(g)
  • 条件 S(g)<mS(g) < m 保证了序列可以变为非递减。
  • 高效处理类型 1 更新确保了系统的性能。

提交链接