[R70E] 裁决

  • 难度 普及/提高-
  • 时限 2s
  • 空限 512m
  • 数学字符串

数据规模

n2000n \le 2000,需要计算全部 n2n^2W(i,j)W(i, j)。暴力枚举 i,ji, j 后逐轮求和是 O(n3)O(n^3),不可行;O(n2)O(n^2)O(n2logn)O(n^2 \log n) 可行。

思路

只与差值有关。记 d=(ji)modnd = (j - i) \bmod n,则第 kk 轮第一名玩家选择 s[(i+k)modn]s[(i+k) \bmod n],第二名玩家选择 s[(j+k)modn]=s[(i+k+d)modn]s[(j+k) \bmod n] = s[(i+k+d) \bmod n]。令 t=(i+k)modnt = (i+k) \bmod n,这一轮相当于:第一名出 s[t]s[t],第二名出 s[(t+d)modn]s[(t+d) \bmod n],获胜时得分 k=(ti)modnk = (t - i) \bmod n

于是对固定的 dd,定义胜者集合

Pd={t[0,n)  |  s[t] 战胜 s[(t+d)modn]}P_d = \left\{ t \in [0, n) \;\middle|\; s[t] \text{ 战胜 } s[(t + d) \bmod n] \right\}

W(i,j)=tPd((ti)modn)W(i, j) = \sum_{t \in P_d} ((t - i) \bmod n)

展开公式(ti)modn(t - i) \bmod ntit \ge i 时为 tit - i,在 t<it < i 时为 ti+nt - i + n,因此

W(i,j)=tPdtiPd+n{tPd:t<i}W(i, j) = \sum_{t \in P_d} t - i \cdot |P_d| + n \cdot \left|\{t \in P_d : t < i\}\right|

增量计算。对每个 d[0,n)d \in [0, n) 先算出 md=Pdm_d = |P_d|sumd=tPdt\mathrm{sum}_d = \sum_{t \in P_d} t。然后按 i=0,1,,n1i = 0, 1, \dots, n-1 扫描:维护 cntd={tPd:t<i}\mathrm{cnt}_d = |\{t \in P_d : t < i\}|,每处理完一行 ii,检查 t=it = i 是否属于 PdP_d,若是则对所有 ddcntd\mathrm{cnt}_d 加 1。每个 dd 的贡献是 O(1)O(1),答案 W(i,(i+d)modn)W(i, (i+d) \bmod n) 直接写入第 ii 行第 (i+d)modn(i+d) \bmod n 列。

复杂度:O(n2)O(n^2) 时间,O(n)O(n) 空间(输出本身是 n2n^2 个数)。

仓颉实现

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

func beats(x: UInt8, y: UInt8): Bool {
    return (x == 'A'[0] && y == 'B'[0]) || (x == 'B'[0] && y == 'C'[0]) || (x == 'C'[0] && y == 'A'[0])
}

main() {
    let reader = getStdIn()
    let n = Int64.parse(reader.readln().getOrThrow())
    let s = reader.readln().getOrThrow()
    let nn = n
    let mCnt = Array<Int64>(nn, { _ => 0 })
    let sumAll = Array<Int64>(nn, { _ => 0 })
    var d: Int64 = 0
    while (d < nn) {
        var m: Int64 = 0
        var sm: Int64 = 0
        var t: Int64 = 0
        while (t < nn) {
            let td = (t + d) % nn
            if (beats(s[t], s[td])) {
                m = m + 1
                sm = sm + t
            }
            t = t + 1
        }
        mCnt[d] = m
        sumAll[d] = sm
        d = d + 1
    }
    let cntLt = Array<Int64>(nn, { _ => 0 })
    let sb = StringBuilder()
    var i: Int64 = 0
    while (i < nn) {
        var j: Int64 = 0
        var line = StringBuilder()
        while (j < nn) {
            let dd = (j - i + nn) % nn
            let w = sumAll[dd] - i * mCnt[dd] + nn * cntLt[dd]
            if (j > 0) {
                line.append(" ")
            }
            line.append(w)
            j = j + 1
        }
        sb.append(line.toString())
        sb.append("\n")
        var d3: Int64 = 0
        while (d3 < nn) {
            let td = (i + d3) % nn
            if (beats(s[i], s[td])) {
                cntLt[d3] = cntLt[d3] + 1
            }
            d3 = d3 + 1
        }
        i = i + 1
    }
    print(sb.toString())
}