题意:
分析:
其实如果会了Ural 1057. Amount of Degrees那道题目,这道题自然也就会了...
我们考虑枚举第$k$个数字的$1$的个数,那么我们需要计算的也就是区间内二进制状态下$1$的个数为$x$的数字个数,这个的求法在上一题中写过了...
我们求到第$k$的数字的$1$的个数为$x$,那么我们去二分这个数字是什么,也就是说我们要求一个最靠左的右端点,使得区间$[n,ans]$内$1$的个数为$x$的数字个数恰好为$k$,然后总体思路就解决了...
细节方面就是要注意特判$0$,然后对于负数的处理就是先去掉负数的符号位,然后判断,最后输出的时候再把符号位加上...
具体实现看代码...
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;
const int maxn=32+5;
int n,m,k,ans,cas,f[maxn][maxn];
inline void init(void){
f[0][0]=1;
for(int i=1;i<=31;i++){
f[i][0]=1;
for(int j=1;j<=i;j++)
f[i][j]=f[i-1][j]+f[i-1][j-1];
}
}
inline int calc(int x,int y){
int cnt=0,ans=0;
for(int i=31;i>=1;i--){
if((x>>i)&1){
cnt++;
if(cnt>y) break;
x=x^(1<<i);
}
if((1<<(i-1))<=x)
ans+=f[i-1][y-cnt];
}
if(cnt+x==y) ans++;
return ans;
}
inline int solve(void){
int len=0;
for(int i=0,tmp;i<=31;i++){
tmp=calc(m,i)-calc(n-1,i);
if(tmp>=k) break;
k-=tmp;len=i+1;
}
long long l=n,r=m,ans;
while(l<=r){
long long mid=(l+r)>>1;
if(calc(mid,len)-calc(n-1,len)>=k)
ans=mid,r=mid-1;
else
l=mid+1;
}
return ans;
}
signed main(void){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
scanf("%d",&cas);init();
while(cas--){
scanf("%d%d%d",&n,&m,&k);
if(m==0&&n==0){
puts("0");
continue;
}
int flag=0;
if(n==0) n++,k--;
if(m==0) m--,k--;
if(n<0) n^=(1<<31),flag=1;
if(m<0) m^=(1<<31),flag=1;
ans=solve();
if(flag) printf("%d\n",(ans^(1<<31)));
else printf("%d\n",ans);
}
return 0;
}
By NeighThorn