[R46F]环操作
题目
给定两个长度为 的循环整数序列 和 。可以执行任意次操作:选择下标 和整数 (可为负),令 ,,,代价 。目标是让 ,求最小总代价,无解输出 。
对于 的数据,,,,。
思路
把「在下标 处做的所有操作」合并记作一个总操作值 (每个位置的代价之和就是 ,总代价为 )。由于操作的差分模板是 ,目标成立的充要条件是
记 。
做一阶差分降阶:令 ,则上式化为 。对下标求和立刻得到必要条件 (也就是 );不满足直接输出 。
满足后,可取特解 ,。注意 本身可以整体平移一个常数 (平移不改变 ),这会带来第二个自由度,用以保证还原出的 仍是循环的。
还原 并补循环条件:从 开始,。循环要求 ,即 。先用平移自由度满足它:取 。由于操作值必须是整数, 必须能被 整除,否则无解输出 。把每个 都加上 后,重新累计得到一组循环整数特解 (仍以 为基准)。
分析剩余自由度:如果另一个解 满足同样的差分方程,则 满足 ,即一阶差分 为常数 ;循环条件 强制 ,故 , 为常向量。所以解空间只有 一个平移自由度 。
最小化代价:目标变成
这是经典的中位数问题,取 ,对排序后的 取 即可。
复杂度
- 时间复杂度: 每组(瓶颈为排序)。
- 空间复杂度: 每组。
仓颉实现
查看仓颉实现
import std.env.*
import std.sort.*
// 快速整型读取:一次性读入全部输入,按字节解析
class FastReader {
let data: String
var idx: Int64
init(data: String) {
this.data = data
this.idx = 0
}
func nextInt(): Int64 {
let n = Int64(data.size)
while (idx < n && data[idx] <= 32u8) {
idx += 1
}
var neg = false
if (idx < n && data[idx] == 45u8) {
neg = true
idx += 1
}
var v: Int64 = 0
while (idx < n && data[idx] >= 48u8 && data[idx] <= 57u8) {
v = v * 10 + Int64(data[idx] - 48u8)
idx += 1
}
if (neg) {
v = -v
}
return v
}
}
main() {
let reader = FastReader(getStdIn().readToEnd().getOrThrow())
let t = reader.nextInt()
for (_ in 0..t) {
let n = reader.nextInt()
// 设 x_i 为下标 i 处操作的值,则需 x_i - 2x_{i-1} + x_{i-2} = b_i - a_i
let a = Array<Int64>(n, { _ => 0 })
for (i in 0..n) {
a[i] = reader.nextInt()
}
var sumD: Int64 = 0
// 令 y_i = x_i - x_{i-1},则 d_i = y_i - y_{i-1}
// y[0] = 0, y[i] = y[i-1] + d[i],Σd = 0 保证 (I-S)y = d 有整数解
let y = Array<Int64>(n, { _ => 0 })
var sumY: Int64 = 0
var prevY: Int64 = 0
for (i in 0..n) {
let di = reader.nextInt() - a[i]
sumD += di
if (i >= 1) {
prevY = prevY + di
y[i] = prevY
sumY += prevY
}
}
if (sumD != 0) {
println(-1)
continue
}
// 还需 Σ(y + c) = 0 有整数 c(循环边界条件)
if (sumY % n != 0) {
println(-1)
continue
}
let cy = -sumY / n
// 还原一组特解 x:x[0] = 0, x[i] = x[i-1] + y[i] + cy
let x = Array<Int64>(n, { _ => 0 })
var prevX: Int64 = 0
for (i in 1..n) {
prevX = prevX + y[i] + cy
x[i] = prevX
}
// 通解为 x + c,最小化 Σ|x_i + c|,c 取中位数
sort(x)
let m = x[n / 2]
var ans: Int64 = 0
for (i in 0..n) {
var diff = x[i] - m
if (diff < 0) {
diff = -diff
}
ans += diff
}
println(ans)
}
}