题目:将区间[m,n]内的所有整数按照其二进制表示中 1 的数量从小到大排序。如果 1 的数量 相同,则按照数的大小排序。求这个序列中的第 k个数。其中,负数使用补码来表示:一个 负数的二进制表示与其相反数的二进制之和恰好等于2^32。 m*n>=0
思路:枚举1的个数,然后找到区间内含有若干个1的数量,最终得到第K个数包含几个1。这样就确定出了答案中包含了len个1。
对于m和n是负数的情况,最高位都为1,我们先把最高位的1去掉,就转化成了正数,然后再进行统计,最后输出的时候把最高位加上即可。
注意0要特判。
代码:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
int dp[35][35];
void Init(){
mm(dp,0);
dp[0][0]=1;
for(int i=1;i<=31;i++){
dp[i][0]=dp[i][i]=1;
for(int j=1;j<i;j++)
dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
}
}
int cal(int n,int k){
int res=0,tot=0;
for(int i=31;i>=1;i--){
if(n&(1<<(i-1))){
res+=dp[i-1][k-tot];
tot++;
if(tot>k)
break;
}
}
if(tot==k) res++;
return res;
}
int solve(int L,int R,int K){
int ans=0,l=L,r=R,len=1;
for(int i=1;i<=31;i++){
int now=cal(R,i)-cal(L-1,i);
if(K<=now)
break;
K-=now;
len=i+1;
}
while(l<=r){
int mid=((LL)l+(LL)r)/2;
if(cal(mid,len)-cal(L-1,len)>=K){
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
return ans;
}
int main(){
int T,m,n,k;
Init();
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&m,&n,&k);
if(m==0&&n==0){
printf("%0\n");
continue;
}
if(m>=0){
if(m==0){
m++;
k--;
}
if(k==0){
printf("0\n");
continue;
}
int ans=solve(m,n,k);
printf("%d\n",ans);
}
else{
if(n==0){
n=-1;
k--;
}
m=m&(~(1<<31));
n=n&(~(1<<31));
int ans=solve(m,n,k);
printf("%d\n",ans|(1<<31));
}
}
return 0;
}