题目描述
原题链接:LeetCode 3007.价值和小于等于K的最大数字
解题思路
由于 1 ≤ k ≤ 1 0 15 1 \le k \le 10^{15} 1≤k≤1015,所以要控制时间复杂度在 o ( l o g n ) o(logn) o(logn)。因此考虑利用二分进行查找,而且要控制每一次验证的时间复杂度在常数范围内。
计算 [ 1 , n u m ] [1,num] [1,num]的每个数的价值和
考虑用一个函数来计算 [ 1 , n u m ] [1,num] [1,num]的每个数的第 k x kx kx位的1的个数的和。再将所有的 k x kx kx位都加起来得到 [ 1 , n u m ] [1,num] [1,num]的每个数的价值和。
计算 [ 1 , n u m ] [1,num] [1,num]的每个数的第 i i i位的1个数的和
这里以6为例子,0-6的所有二进制数有:
000
001
010
011
100
101
110
最低位:
[
1
,
n
u
m
]
[1,num]
[1,num]的所有奇数的个数=
n
u
m
/
2
+
n
u
m
&
1
num/2+num \& 1
num/2+num&1
次低位:我们可以将每个数都右移1位,得到:
00
01
10
11
00
01
10
可以看出有2个01,2个10,以及2个00和1个11。
最高位:同样的方法,将每个数都右移2位,得到:
0
0
1
1
0
0
1
可以看出有3个1和4个0。
我们可以发现,当最高位为1也就是6这个数的最高位时,后面两位最高只能到10也就是6的后两位,也就是2,这时一共有2+1=3种可能。而当前面两位为11时,最后一位最多也只能跟6的最后一位相同,也就是0。除开这两种跟6相同的情况,其他情况例如前面两位为10时,最后一位可以是1或0;当最高位为0时,后面两位可以是00,01,10,11四种情况。这时我们观察到当第
i
i
i位及之前的数构成的二进制数比相同位置的
n
u
m
num
num数小。
综合上面的结论,我们就可以令
n
=
n
u
m
/
2
i
n=num/2^{i}
n=num/2i,统计
[
1
,
n
−
1
]
[1,n-1]
[1,n−1]的奇数个数,再将这些个数乘以
2
i
2^{i}
2i得到初步的result。再计算
n
n
n是否为奇数,如果是,则再将
n
n
n后面能填的数的个数加上。
long long count_bitsum(long long num, int i) {
long long res = 0;
long long period = 1ll << i;
long long n = num >> i;
res += (n / 2) * period; //计算[1,n-1]的奇数个数并乘以可以以这些奇数为开头后面能填的数的个数
res += (num % period + 1) * (n & 1); //计算跟num前i位相同的时候后面能填的数的个数以及num的前i位是否是一个奇数
return res;
}
完整代码
class Solution {
public:
long long count_bitsum(long long num, int i) {
long long res = 0;
long long period = 1ll << i;
long long n = num >> i;
res += (n / 2) * period;
res += (num % period + 1) * (n & 1);
return res;
}
long long count_sum(long long num, int x) {
long long res = 0;
int length = 64 - __builtin_clzll(num);
for(int i = x-1; i <= length; i += x) {
res += count_bitsum(num, i);
}
return res;
}
long long findMaximumNumber(long long k, int x) {
long long l = 1, r = (k+1) << x;
while(l <= r) {
long long mid = (l+r) >> 1;
if(count_sum(mid, x) <= k) {
l = mid+1;
} else {
r = mid-1;
}
}
return r;
}
};