目录
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
914C - Travelling Salesman and Special Numbers
二、解题报告
1、思路分析
考虑 定义 f(x) 为 x 变为 1 所需的操作次数
那么 f(x) = f(pcnt(x)) + 1
我们注意到 虽然 n 很大,但是 对于 f(x) = k 的数字,其递推都是由 f(pcnt(x)) 转移,而pcnt(x) 很小,不超过1000
所以我们只需计算出 [0, 1000] 的 f(),然后 对于 f(x) = k - 1的 x,我们找 [0, n] 之内 有多少数的pcnt = x,这件事情可以用数位dp轻松解决
具体实现中,f() 我们可以初始化 f[0] = 1,从而原来的找 f(x) = k - 1 就 变为了 找 f(x) = k
2、复杂度
时间复杂度: O(N^2)空间复杂度:O(N^2)
3、代码详解
#include <bits/stdc++.h>
// #define DEBUG
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
const int P = 1'000'000'007;
const int N = 1000;
int memo[N + 1][N + 1];
void solve() {
std::string s;
std::cin >> s;
int k;
std::cin >> k;
if (k == 0) {
std::cout << "1\n";
return;
}
int n = s.size();
if (k == 1) {
std::cout << n - 1 << '\n';
return;
}
memset(memo, -1, sizeof memo);
auto dfs = [&](auto &&self, int i, int pre, bool lim) -> int{
if (pre == 0) {
return 1;
}
if (i == n) {
return 0;
}
if (lim && ~memo[i][pre]) {
return memo[i][pre];
}
int top = lim ? 1 : (s[i] ^ 48);
int res = 0;
for (int d = 0; d <= top; ++ d) {
res += self(self, i + 1, pre - d, lim || d < top);
if (res >= P) {
res -= P;
}
}
if (lim) {
memo[i][pre] = res;
}
return res;
};
int res = 0;
std::vector<int> f(n + 1);
for (int i = 1; i <= n; ++ i) {
f[i] = f[__builtin_popcount(i)] + 1;
if (f[i] == k) {
res += dfs(dfs, 0, i, false);
if (res >= P) {
res -= P;
}
}
}
std::cout << res << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
#ifdef DEBUG
int cur = clock();
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int t = 1;
// std::cin >> t;
while (t--) {
solve();
}
#ifdef DEBUG
std::cerr << "run-time: " << clock() - cur << '\n';
#endif
return 0;
}