[R57C] RhythmName
- 难度 普及/提高-
- 时限 1s
- 空限 512m
- 数学前缀和
数据规模:,,。
思路
成绩 的值域只有 ,共 个取值,所以可以按值统计频次 ,再对每个查询 计算
直接枚举 是 每次,会超时。把平方展开:
于是
预处理三个前缀和数组,下标 表示值落在 内的累计:
- ;
- ;
- 。
区间 内的频次、和、平方和分别用 得到,代入公式即得答案;区间内无成绩时输出 。
数值上界: 最大约 , 最大约 ,都在 Int64 范围内。
复杂度:时间 ,空间 ,其中 。
仓颉实现
import std.env.*
import std.convert.*
main(): Int64 {
let reader = getStdIn()
let first = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let q = first[1]
let ts = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
// 成绩值域只有 0..10000,按值统计频次
let maxv = 10000
let m = maxv + 2
let cnt = Array<Int64>(m, { _ => 0 })
let sum = Array<Int64>(m, { _ => 0 })
let sq = Array<Int64>(m, { _ => 0 })
for (t in ts) {
let u = Int64(t)
cnt[u + 1] = cnt[u + 1] + 1
sum[u + 1] = sum[u + 1] + u
sq[u + 1] = sq[u + 1] + u * u
}
// 前缀和:pre[i] = 值落在 [0, i-1] 内的累计
for (i in 1..m) {
cnt[i] = cnt[i] + cnt[i - 1]
sum[i] = sum[i] + sum[i - 1]
sq[i] = sq[i] + sq[i - 1]
}
let sb = StringBuilder()
for (i in 0..q) {
let qline = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let l = qline[0]
let r = qline[1]
let c = cnt[r + 1] - cnt[l]
let s = sum[r + 1] - sum[l]
let ss = sq[r + 1] - sq[l]
let ans = if (c > 0) { ss - 2 * l * s + l * l * c } else { 0 }
sb.append(ans)
sb.append("\n")
}
print(sb.toString())
return 0
}