[R71C] 区间校温

  • 难度 入门
  • 时限 1s
  • 空限 256m
  • 枚举前缀最值

思路

先统计初始同步对数量 base

关键观察:对区间 [l,r][l, r] 内部的相邻对 (i,i+1)(i, i+1)li<rl \le i < r),两个温度都加 11,同步关系不变;区间外的相邻对也不变。真正可能变化的只有两个边界对:(l1,l)(l-1, l)(若 l>1l > 1)和 (r,r+1)(r, r+1)(若 r<nr < n)。

定义左端点贡献与右端点贡献:

L(l)={[al1=al+1][al1=al],l>10,l=1L(l) = \begin{cases} [a_{l-1}=a_l+1] - [a_{l-1}=a_l], & l > 1 \\ 0, & l = 1 \end{cases}

即左侧被加 11 后,边界对 (l1,l)(l-1, l) 的同步状态变化量:原本差 11 则变为同步(+1+1),原本相等则变为不同步(1-1)。

R(r)={[ar=ar+11][ar=ar+1],r<n0,r=nR(r) = \begin{cases} [a_r=a_{r+1}-1] - [a_r=a_{r+1}], & r < n \\ 0, & r = n \end{cases}

同理是右侧边界对 (r,r+1)(r, r+1) 的变化量。答案即为

base+max1lrn(L(l)+R(r))base + \max_{1 \le l \le r \le n} \left( L(l) + R(r) \right)

直接枚举 l,rl, rO(n2)O(n^2)。由于 LLRR 相互独立,只受 lrl \le r 约束,可以扫描右端点 rr,同时维护前缀最大值 mx=max(L(1),,L(r))\text{mx} = \max(L(1), \ldots, L(r)),每次用 mx+R(r)\text{mx} + R(r) 更新答案即可。

复杂度:时间 O(n)O(n),空间 O(n)O(n)

仓颉实现

import std.convert.*
import std.env.*

main() {
    let reader = getStdIn()
    let n = Int64.parse(reader.readln().getOrThrow())
    let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
    var base: Int64 = 0
    var i: Int64 = 0
    while (i + 1 < n) {
        if (a[i] == a[i + 1]) {
            base += 1
        }
        i += 1
    }
    var maxL: Int64 = 0
    var best: Int64 = -2
    i = 0
    while (i < n) {
        if (i >= 1) {
            var li: Int64 = 0
            if (a[i - 1] == a[i] + 1) {
                li = 1
            }
            if (a[i - 1] == a[i]) {
                li -= 1
            }
            if (li > maxL) {
                maxL = li
            }
        }
        var ri: Int64 = 0
        if (i + 1 < n) {
            if (a[i] == a[i + 1] - 1) {
                ri = 1
            }
            if (a[i] == a[i + 1]) {
                ri -= 1
            }
        }
        let cur = maxL + ri
        if (cur > best) {
            best = cur
        }
        i += 1
    }
    println(base + best)
}