[R28C]最大操作数


1≤n≤2×1051 \le n \le 2 \times 10^5,1≤Ai,Bi≤1091 \le A_i, B_i \le 10^9,0≤Y≤1090 \le Y \le 10^9。

思路

递推式为 Xi=⌊Xi−1/Ai⌋−BiX_i = \lfloor X_{i-1}/A_i \rfloor - B_i,已知末项 Xn=YX_n = Y,要求使整条链合法的最大 X0X_0。

把递推式反过来:⌊Xi−1/Ai⌋=Xi+Bi\lfloor X_{i-1}/A_i \rfloor = X_i + B_i。设 q=Xi+Biq = X_i + B_i,则满足 ⌊t/Ai⌋=q\lfloor t / A_i \rfloor = q 的整数 tt 恰好构成区间

[Ai⋅q, Ai⋅(q+1)−1].[A_i \cdot q,\ A_i \cdot (q+1) - 1].

要让最终的 X0X_0 尽量大,每一步倒推时都应该取这个区间的右端点,于是得到一个确定的反向递推:

Xi−1=Ai⋅(Xi+Bi+1)−1.X_{i-1} = A_i \cdot (X_i + B_i + 1) - 1.

这是一个关于 XiX_i 的 仿射函数(形如 f(x)=mx+cf(x) = m x + c)。从 Xn=YX_n = Y 倒推到 X0X_0,相当于把若干个仿射函数复合起来,结果仍是关于 YY 的仿射函数:

X0max⁡=a⋅Y+b.X_0^{\max} = a \cdot Y + b.

只要维护这组系数 (a,b)(a, b),并对 p=998244353p = 998244353 取模即可,无需关心 X0X_0 真实有多大。复合规则:若当前 Xi=aY+bX_i = aY + b,则

Xi−1=Ai(aY+b+Bi+1)−1=(Ai⋅a)Y+(Ai(b+Bi+1)−1),X_{i-1} = A_i(aY + b + B_i + 1) - 1 = (A_i \cdot a) Y + (A_i(b + B_i + 1) - 1),

即

a←Ai⋅a,b←Ai⋅(b+Bi+1)−1.a \leftarrow A_i \cdot a,\qquad b \leftarrow A_i \cdot (b + B_i + 1) - 1.

初值 a=1, b=0a = 1,\ b = 0(代表 Xn=YX_n = Y),从 i=ni = n 倒推到 i=1i = 1,最后答案为 (a⋅Y+b) mod p(a \cdot Y + b) \bmod p。

注:每步都取右端点能保证全局最优,因为反向递推每一步都是 Xi−1X_{i-1} 关于 XiX_i 的严格递增函数,前一步取更大值传递下去一定让最终结果更大。

复杂度

  • 时间:O(n)O(n),一次倒推遍历。
  • 空间:O(n)O(n),存储数组 AA、BB。

仓颉实现

查看仓颉实现
import std.convert.*
import std.env.*

main() {
    let reader = getStdIn()
    let n = Int64.parse(reader.readln().getOrThrow())
    let aArr = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    let bArr = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    let y = Int64.parse(reader.readln().getOrThrow())
    let mod = 998244353
    // 倒推:X_{i-1}_max = A_i*(X_i + B_i + 1) - 1,是关于 X_i 的仿射函数。
    // 维护最终 X_0_max = a*Y + b (mod p),初始 a=1, b=0。
    var a = Int64(1)
    var b = Int64(0)
    var i = n - 1
    while (i >= 0) {
        let ai = aArr[i] % mod
        let bi = bArr[i]
        // new_a = A_i * a ; new_b = A_i * (b + B_i + 1) - 1
        a = (ai * a) % mod
        b = (ai * ((b + bi + 1) % mod) % mod + mod - 1) % mod
        i = i - 1
    }
    let ans = ((a * (y % mod)) % mod + b) % mod
    println(ans)
}