[R31B]删除数字
对于 的数据,,,,所有测试数据的 的和不超过 。
思路
设数组总和为 ,只关心 。删去若干元素后,剩余和仍为整数,要让它变成 的倍数,等价于让删去元素之和恰好抵消 。
对每个 只关心它对 取模的余数 ,统计余数 的个数 、余数 的个数 (余数 的元素删不删不影响总和模 )。
- 若 ,已经满足,删除 个。
- 若 ,需要让删去和 :要么删 个余 的数,要么删 个余 的数()。优先删 个;当 时只能删 个。
- 若 ,对称地:要么删 个余 的数,要么删 个余 的数()。优先删 个;当 时只能删 个。
可行性显然:当 时,,且若 则 (否则 不会是 ),故两种情形下总存在合法方案,答案不超过 。
复杂度
每组数据扫描一次数组,时间 ,空间 (读入数组)。总时间 。
仓颉实现
import std.console.*
import std.convert.*
// [R31B] 删除数字
// 数组 A,删除最少的元素使剩余元素之和是 3 的倍数。
// 总和 S%3:0 删 0;1 删 1 个 %3==1 或 2 个 %3==2;2 删 1 个 %3==2 或 2 个 %3==1。
func solve(reader: ConsoleReader): Int64 {
let n = Int64.parse(reader.readln().getOrThrow())
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
var c0 = 0
var c1 = 0
var c2 = 0
var s = 0
for (x in a) {
let r = x % 3
s = (s + r) % 3
if (r == 0) {
c0 = c0 + 1
} else if (r == 1) {
c1 = c1 + 1
} else {
c2 = c2 + 1
}
}
if (s == 0) {
return 0
} else if (s == 1) {
// 删 1 个 %3==1,或删 2 个 %3==2,取较小(数量不够则只能选另一方案)
if (c1 >= 1) {
return 1
}
return 2
} else {
// s == 2:删 1 个 %3==2,或删 2 个 %3==1
if (c2 >= 1) {
return 1
}
return 2
}
}
main(): Int64 {
let reader = Console.stdIn
let t = Int64.parse(reader.readln().getOrThrow())
for (_ in 0..t) {
println(solve(reader))
}
return 0
}