[R70D] 溯源
- 难度 普及/提高-
- 时限 2s
- 空限 512m
- 数学位运算
思路
先化简 。按二进制展开 (),则 ,所以
其中 是 的二进制中 1 的个数。
反向链。 严格递增,且 (等号仅当 ),因此给定 ,满足 的 至多一个:若存在,则 ( 时是自身原像)。所以从 出发不断求原像,路径唯一,且每一步数值至少减半,链长 。能到达 的初始值必然在这条反向链上,链上最小元素(即链尾)就是答案;若某个值没有原像,它本身就是答案(不操作也满足条件)。
求原像。由 得 。枚举 ():要求 为偶数,即 ,只需枚举约 30 个同奇偶的值;算出 后检查 即可。
复杂度:链长与 popcount 枚举都是 ,每次枚举内 popcount 为 位运算,总复杂度 ,空间 。
仓颉实现
import std.convert.*
import std.env.*
func popcount(v0: Int64): Int64 {
var v = v0
v = v - ((v >> 1) & 0x5555555555555555)
v = (v & 0x3333333333333333) + ((v >> 2) & 0x3333333333333333)
v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0F
v = v + (v >> 8)
v = v + (v >> 16)
v = v + (v >> 32)
return v & 0x7F
}
func solve(): Unit {
let reader = getStdIn()
var y = Int64.parse(reader.readln().getOrThrow())
while (true) {
var nx = y
var pc: Int64 = 1
if (y % 2 == 0) {
pc = 2
}
while (pc <= 60) {
let cand = (y + pc) / 2
if (popcount(cand) == pc) {
nx = cand
break
}
pc = pc + 2
}
if (nx >= y) {
break
}
y = nx
}
println(y)
}
main() {
let reader = getStdIn()
let t = Int64.parse(reader.readln().getOrThrow())
var i = 0
while (i < t) {
solve()
i = i + 1
}
}