point:
观察可知,只要字符串是0的位置变为1,后面的数字就是一个1、0任意的排列。
这样,我们先从后开始找0,找到0,假设他为1,然后算一下有几个排列组合,如果<当前的k。那么k-=这个排列组合。
在往前找。直到找到>=k的。
然后问题就是当前位置(假设为n)的后面(len-n)个0、1排列要怎么排了。从前往后贪心假设,先假设为0,算一下排列组合,够了就是0了,不够就是1了(这个情况要更新k)。这样就把这个数贪心的找出来了。
排列组合打个表就是。我的代码C[0][0]要为1.
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
#define LL long long
LL C[70][70];
void init()
{
C[0][0]=1;
for(LL i=1;i<=64;i++)
C[i][1]=i,C[i][i]=C[i][0]=1;
for(LL i=3;i<=64;i++){
for(LL j=2;j<i;j++){
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
// for(LL i=1;i<=64;i++){
// for(LL j=1;j<=i;j++){
// printf("%lld ",C[i][j]);
// }
// printf("\n");
// }
}
LL ans[70];
int main()
{
init();
LL T;
scanf("%lld",&T);
LL cas=0;
LL one[70],zero[70];
LL num[70];
while(T--){
for(int i=0;i<=64;i++){
one[i]=zero[i]=0;
num[i]=ans[i]=0;
}
LL n,k;
scanf("%lld %lld",&n,&k);
LL wei=0;
while(n){
num[wei+1]=n&1;
if(n&1) one[++wei]++;
else zero[++wei]++;
n>>=1;
}
for(LL i=1;i<=wei;i++){
one[i]+=one[i-1];
zero[i]+=zero[i-1];
}
LL now = 2;
while(k&&now<=wei){
if(num[now]!=0){
now++;
continue;
}else{
LL o=one[now],z=zero[now];
o--;
LL sum=o+z;
if(C[sum][o]<k){
k-=C[sum][o];
now++;
continue;
}else{
ans[now]=1;
while(k&&now>1){
//如果now-1这个位置放0.
LL zz=z-1;
if(C[o+zz][o]>=k){
ans[now-1]=0;
z=zz;
now--;
}else{
k-=C[o+zz][o];
ans[now-1]=1;
o--;
now--;
}
}
k=0;
break;
}
}
}
printf("Case #%lld: ",++cas);
if(k!=0){
printf("IMPOSSIBLE\n");
}else{
LL i;
LL aim=0;
for(i=wei;ans[i]!=1;i--){
if(num[i]){
aim|=(1LL<<(i-1));
}
}
for(;i>=1;i--){
if(ans[i]){
aim|=(1LL<<(i-1));
}
}
printf("%lld\n",aim);
}
}
return 0;
}