特殊二进制数
- 难度 提高
- 时限 1s
- 空限 256m
- 数位DP
数据规模:,, 的二进制表示长度为 到 且不含前导零。
思路
把操作次数落到 的个数上。 记 为对整数 反复取 popcount 直到变成 的操作次数,即 ,。由于 (),这个递推可以按 从小到大直接算出。
对任意 ,第一次操作后得到 ,于是
因为 ,,所以预先算好 的 就够用。题意要求 ,等价于要求 且 。问题因此拆成两层:先枚举所有满足 的 ,再数 到 中 1 的个数恰为 的整数。
数出「恰有 个 1」的个数。 设 的二进制串长为 ,从高位到低位扫描,同时维护 表示当前已经与 的前缀完全相同时数到的 1 的个数。当扫描到某一位且 的该位为 1 时,令这一位取 、更前面的位仍与 相同,得到的数就严格小于 ,并且这是「首次变得比 小」的位置。此后剩余 位可以任意填,要让总共有 个 1,还需在其中放 个,方案数为
把所有这样的位置贡献累加,最后补上 自己(当 时 ),就得到不超过 且恰有 个 1 的数的个数。每个数都唯一对应「首次变小的位置」,因此不重不漏。
边界。 时方案里不会数到 ,所以不必额外排除 ;但 对应的 是 的 ,此时 也被计入(它确实只有一个 1),而 ,故答案要减 。 时只有 符合,答案恒为 ( 保证它存在)。另外, 时 ,所以 较大时答案直接为 ,无需特判也不会出错。
组合数。 模数 是质数,且 ,用阶乘与其逆元算 即可。
复杂度
预处理 为 ,阶乘与逆元 。统计部分对每个为 1 的位置枚举 ,总计 次组合数运算,时间 、空间 , 时约 次运算,远小于时限。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
const MOD: Int64 = 1000000007
func popcount(x: Int64): Int64 {
var t = x
var c: Int64 = 0
while (t > 0) {
c += t & 1
t >>= 1
}
return c
}
func qpow(a: Int64, e: Int64): Int64 {
var base = a % MOD
var exp = e
var res: Int64 = 1
while (exp > 0) {
if (exp % 2 == 1) {
res = res * base % MOD
}
base = base * base % MOD
exp >>= 1
}
return res
}
main() {
let reader = getStdIn()
let bits = reader.readln().getOrThrow().split(" ", removeEmpty: true)[0].toRuneArray()
let k = Int64.parse(reader.readln().getOrThrow().split(" ", removeEmpty: true)[0])
let n = bits.size
if (k == 0) {
println(1)
} else {
let steps = Array<Int64>(1001, { _ => 0 })
for (c in 2..=1000) {
steps[c] = 1 + steps[popcount(c)]
}
let fact = Array<Int64>(n + 1, { _ => 1 })
for (i in 1..=n) {
fact[i] = fact[i - 1] * i % MOD
}
let invfact = Array<Int64>(n + 1, { _ => 1 })
invfact[n] = qpow(fact[n], MOD - 2)
var i = n
while (i > 0) {
invfact[i - 1] = invfact[i] * i % MOD
i -= 1
}
let cnt = Array<Int64>(n + 2, { _ => 0 })
var used: Int64 = 0
for (p in 0..n) {
if (bits[p] == r'1') {
let r = n - 1 - p
for (j in 0..=r) {
let comb = fact[r] * invfact[j] % MOD * invfact[r - j] % MOD
cnt[used + j] = (cnt[used + j] + comb) % MOD
}
used += 1
}
}
cnt[used] = (cnt[used] + 1) % MOD
var ans: Int64 = 0
for (c in 1..=n) {
if (steps[c] == k - 1) {
ans = (ans + cnt[c]) % MOD
}
}
if (k == 1) {
ans = (ans - 1 + MOD) % MOD
}
println(ans)
}
}