[R46D]四元组
数据规模:,。
思路
要求统计满足 且 的四元组个数。直接 枚举四元组显然超时,需要降维。
把条件 看成:选定中间分割点 (第三元下标),则等号左边是「前缀中某对 ()的和」,等号右边是「 与某个 配对的和」。如果能在线性时间内维护出前缀中每种和出现了多少对,那么固定 后只需对每个 做一次 查询。
维护一个桶 ,表示当前前缀里 ()的对数。从左到右扫 ,对每个 :
- 枚举 ,把 累加进答案;
- 处理完 后,把所有以 为右端点的新对 ()的和加入桶,为后续更大的 服务。
初始时桶里只有 这一对。整个扫描过程中,每对 恰好在 成为 之后被加入一次,每对 恰好被查询一次,总工作量为 。由于 ,桶大小开 即可覆盖所有可能的和。
复杂度
时间 ,空间 。
仓颉实现
import std.env.*
import std.convert.*
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let nn = n
let size = 4001
var cnt2 = Array<Int64>(size, { _ => 0 })
var ans: Int64 = 0
// Invariant at the start of each iteration with index k: cnt2 holds the
// pair-sum counts of all (i, j) with i < j < k.
// Initialize with the only pair whose right endpoint < 2, namely (0, 1).
cnt2[a[0] + a[1]] = 1
var k: Int64 = 2
while (k <= nn - 2) {
// query every l > k
var l: Int64 = k + 1
while (l < nn) {
ans += cnt2[a[k] + a[l]]
l += 1
}
// promote k: pairs whose right endpoint is k now join the prefix, so that
// for the next k the invariant (i < j < k) still holds.
var i: Int64 = 0
while (i < k) {
cnt2[a[i] + a[k]] += 1
i += 1
}
k += 1
}
println(ans.toString())
return 0
}