[R37D]斜三进制
题目
猫头鹰国的数只由 四个数字组成,定义后继规则如下:
- 若数中存在数字 (这样的位唯一),则把该位替换为 ,并将更高的一位加一;
- 否则只把个位加一。
按此规则从 开始计数,前若干个数为 。给定一个长度为 的合法猫头鹰国数串 ,请把它翻译成十进制数。
对于 的数据,,,且 是合法的猫头鹰国数。
思路
关键在于识别这是 斜三进制(skew ternary / biased ternary):每位允许的数字是 ,但进位不是逢三进一,而是「出现 3 时整体消去并向上跳」,因此每位的位权不是 ,而是一个等比 +1 的递推序列。
设第 位(从个位起 )的位权为 。由后继规则可推出递推:当某位从 变 时,相当于该位的贡献从 跌回 ,而更高位 贡献 ,要保持连续的后继关系必须有
解得显式
于是任意猫头鹰国数 ( 为个位)的十进制值为
用样例验证:
- ;
- 。
合法猫头鹰国数保证了「3 至多出现一次」等约束,但对于翻译这道题,由于题面保证输入合法,直接按上述位权求和即可,无需校验合法性。
规模上 ,最大位权 ,总和不超过约 ,Int64 足够。
复杂度
- 时间复杂度:,从个位向高位扫描一遍累加。
- 空间复杂度: 存输入串,位权边算边递推无需数组。
仓颉实现
import std.console.*
import std.convert.*
main(): Int64 {
let reader = Console.stdIn
let n = Int64.parse(reader.readln().getOrThrow())
let s = reader.readln().getOrThrow()
// 位权 T_i = (3^(i+1) - 1) / 2,递推 T_{i+1} = 3*T_i + 1,T_0 = 1
// 从个位(字符串末尾)开始处理
var weight: Int64 = 1
var answer: Int64 = 0
var i = n - 1
while (i >= 0) {
let digit = Int64(UInt32(s.toRuneArray()[Int64(i)]) - UInt32(r'0'))
answer += digit * weight
weight = 3 * weight + 1
i -= 1
}
println(answer)
return 0
}