[R67D] 有趣值
- 难度 普及+/提高
- 时限 1s
- 空限 512m
- 排序数学
数据规模:,。
思路
先化简数对的有趣值。若 ,则:
时同理可得 。也就是说,无论大小关系如何,有趣值总是较大数的平方减去较小数的平方。
于是把数组排序,设排序后为 ,对任意 ,数对 的有趣值为 ,总和为:
按元素 统计贡献:作为较大值(与前面 个元素配对)出现 次,贡献 ;作为较小值(与后面 个元素配对)出现 次,贡献 。总贡献系数为:
答案即为:
相同元素无需特殊处理,因为它们的贡献会相互抵消。实现时注意 , 可达 ,需先对 取模再平方;系数 可能为负,取模后要加模数转成正数,再与平方取模相乘并累加。
复杂度:时间 (排序),空间 。
仓颉实现
import std.env.*
import std.convert.*
import std.sort.*
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow().split(" ", removeEmpty: true)[0])
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
sort(a)
let MOD = 1000000007
let nn = n
var ans: Int64 = 0
// 排序后,数对 (i, j) (i < j) 的有趣值为 b_j^2 - b_i^2,
// b_k 作为较大值出现 k-1 次、作为较小值出现 n-k 次,贡献系数为 2k-n-1。
var i: Int64 = 0
while (i < nn) {
let v = a[i] % MOD
let sq = (v * v) % MOD
var coeff = (2 * (i + 1) - nn - 1) % MOD
if (coeff < 0) {
coeff += MOD
}
ans = (ans + coeff * sq) % MOD
i += 1
}
println(ans)
return 0
}
要点:
- 系数 的取值范围约为 ,小于模数,取模后只需加一次模数即可转正。
- 每步乘法前都取模:
coeff * sq与ans + coeff * sq均不超过 ,不会溢出Int64。