[R70E] 裁决
- 难度 普及/提高-
- 时限 2s
- 空限 512m
- 数学字符串
数据规模
,需要计算全部 个 。暴力枚举 后逐轮求和是 ,不可行; 或 可行。
思路
只与差值有关。记 ,则第 轮第一名玩家选择 ,第二名玩家选择 。令 ,这一轮相当于:第一名出 ,第二名出 ,获胜时得分 。
于是对固定的 ,定义胜者集合
则
展开公式。 在 时为 ,在 时为 ,因此
增量计算。对每个 先算出 与 。然后按 扫描:维护 ,每处理完一行 ,检查 是否属于 ,若是则对所有 的 加 1。每个 的贡献是 ,答案 直接写入第 行第 列。
复杂度: 时间, 空间(输出本身是 个数)。
仓颉实现
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())
}