[R19E]区间和
题目
给定整数序列 ,定义 为 中出现奇数次的数字之和。求
对于 的数据,,。
思路
按值分别统计贡献。对每个值 ,它贡献到答案的区间恰好是「 在区间内出现奇数次」的区间,因此只需统计这样的区间数,再乘上 。
设 的出现位置为 ,记 为前 个元素中 出现次数的奇偶性(,)。区间 内 出现奇数次当且仅当 ,所以满足条件的区间数为「奇偶性为 的前缀数」「奇偶性为 的前缀数」。
只在 出现的位置处翻转,故奇偶性为 的前缀覆盖区间
若 为奇数,末尾还有一段 。于是
贡献为 。
实现时把数对 编码成 后排序,同一值的下标自然聚在一起,对每组出现位置用上述公式 均摊地统计即可。注意 可达 ,先对 取模再乘,避免中间结果溢出 Int64。
复杂度
- 时间复杂度:,由排序主导。
- 空间复杂度:。
仓颉实现
import std.env.*
import std.convert.*
import std.sort.*
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let nn = n
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
// 编码 (a_i, i) 为 a_i * n + i,排序后按值分组
var enc = Array<Int64>(nn, { _ => 0 })
for (i in 0..nn) {
enc[i] = a[i] * nn + i
}
sort(enc)
let MOD: Int64 = 998244353
var ans: Int64 = 0
var i: Int64 = 0
while (i < nn) {
let v = enc[i] / nn
var j = i
while (j < nn && enc[j] / nn == v) {
j += 1
}
let m = j - i
// 前缀奇偶性为 1 的前缀个数:
// 每对相邻位置 (p_k, p_{k+1}) 贡献 p_{k+1} - p_k,m 为奇数时最后一段 [p_m, n] 贡献 n - p_m
var cnt1: Int64 = 0
var k: Int64 = 0
while (k + 1 < m) {
cnt1 += (enc[i + k + 1] % nn) - (enc[i + k] % nn)
k += 2
}
if (m % 2 == 1) {
cnt1 += nn - (enc[i + m - 1] % nn)
}
let cnt0 = nn + 1 - cnt1
let c = (cnt0 % MOD) * (cnt1 % MOD) % MOD
ans = (ans + (v % MOD) * c) % MOD
i = j
}
println(ans.toString())
return 0
}