0%

3007. 价值和小于等于 K 的最大数字

3007.价值和小于等于 K 的最大数字

题目描述:

给你一个整数 k 和一个整数 x

s 为整数 num 的下标从 1 开始的二进制表示。我们说一个整数 num价值 是满足 i % x == 0s[i]设置位i 的数目。

请你返回 最大 整数 num ,满足从 1num 的所有整数的 价值 和小于等于 k

注意:

  • 一个整数二进制表示下 设置位 是值为 1 的数位。
  • 一个整数的二进制表示下标从右到左编号,比方说如果 s == 11100 ,那么 s[4] == 1s[2] == 0

数据范围:

$1\le k \le 10^{15}, 1\le x\le 8$

题解:

之前写的数位dp都是统计数的个数的,这个题目也是需要数位dp做,但是需要统计价值。

区别就是 $dp$ 数组的含义,假设 $dp[step][val]$ 为枚举到 $step$ 位,并且该位不受最大值限制,前面价值为 $val$ 时的总价值和。

也就是需要修改模板,在 step == n 时,返回 $val$ ,而不是返回 $!islead$ 。

也要使用二分check,check的时候使用数位dp检查。由于是否存在前导零对答案没有影响,因此可以不用使用 isLead

还需要提前把数字转化为二进制。

最后还剩一个问题:二分的上界取多少合适?

$1$ 到 $num$ 中的数字 $v$ ,除以 $2^{x-1}$ 后如果是奇数,就说明 $v$ 至少包含一个我们需要的 $1$ 。而 $1$ 到 $num$ 中有 $\left\lfloor\dfrac{\textit{num}+1}{2}\right\rfloor$ 个奇数,所以取 $num$ 为 $(k + 1)2^x$ 可以保证价值和至少是 $k+1$ ,一定不满足二分判定。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
string s;
int n;
int x;
long long dp[64][64];
long long dfs(int step, int val, bool isLimit)
{
if (step == n)
return val;
if (!isLimit && dp[step][val] != -1)
return dp[step][val];
long long ans = 0;
int up = isLimit ? s[step] - '0' : 1;
for (int i = 0; i <= up; ++i)
{
ans += dfs(step + 1, val + (i == 1 && ((n - step) % x == 0)), isLimit && i == up);
}
if (!isLimit)
dp[step][val] = ans;
return ans;
}
bool check(long long mid, long long k, int _x)
{
s.clear();
while (mid)
{
if (mid & 1)
s.push_back('1');
else
s.push_back('0');
mid >>= 1;
}
n = s.length();
x = _x;
reverse(s.begin(), s.end());
memset(dp, -1, sizeof(dp));
long long ret = dfs(0, 0, true);
return ret <= k;
}
long long findMaximumNumber(long long k, int x)
{
long long l = 1, r = (k + 1) << x;
long long ans;
while (l <= r)
{
long long mid = l + (r - l >> 1);
if (check(mid, k, x))
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
return ans;
}
};