题意:
给出一个范围,这个范围的数换算成二进制,然后根据1的数目排序从少到多,如果1的数量相同就根据数字的大小排序,大的在后面。求第k大的数。
题解:
数位的统计问题,首先预处理dp[i][j]位数i,1的个数j的数的个数。然后根据区间我们枚举1的个数,计算区间1的个数为i的数的个数,不断累加直到超过k,那么k中1的个数肯定是等于枚举的最后一个i。那么在去区间二分答案。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define B(x) (1LL<<(x)) void cmax(int& a,int b){ if(b>a)a=b; } void cmin(int& a,int b){ if(b<a)a=b; } typedef unsigned long long ll; const int oo=0x3f3f3f3f; const int MOD=1000000007; const int maxn=22; int dp[33][33],k; void DP(){ memset(dp,0,sizeof dp); dp[0][0]=1; for(int i=1;i<=31;i++){ dp[i][0]=dp[i-1][0]; for(int j=1;j<=i;j++){ dp[i][j]=dp[i-1][j]+dp[i-1][j-1]; } } } int calc(int n,int k){ int tol=0,ans=0; for(int i=31;i>=0;i--){ if(n&B(i)){ tol++; if(tol>k)break; n^=B(i); } if(B(i-1)<=n)ans+=dp[i-1][k-tol]; } if(tol+n==k)ans++; return ans; } int solve(int m,int n,int k){ int sum,i; for(i=1;i<=31;i++){ sum=calc(n,i)-calc(m-1,i); if(sum>=k)break; k-=sum; } int l=m,r=n,mid; while(l<r){ mid=((ll)l+(ll)r)>>1; if(calc(mid,i)-calc(m-1,i)<k) l=mid+1; else r=mid; } return l; } int main(){ /* #define ON 1 #ifdef ON freopen("E:\\read.txt","r",stdin); #endif // ON //*/ int T,m,n; DP(); scanf("%d",&T); while(T--){ scanf("%d %d %d",&m,&n,&k); if(n==0&&m==0){ printf("0\n"); continue; } if(m>=0&&n>=0){ if(m==0){ m++; k--; } if(k==0) printf("0\n"); else printf("%d\n",solve(m,n,k)); }else{ if(n==0){ n--; k--; } m&=(~B(31)); n&=(~B(31)); printf("%d\n",B(31)|solve(m,n,k)); } } return 0; } /** */