题解
容易发现,对于给定的(N),假设(y)在(k)个互异的(path_j)中出现,那么在([1,n])内一定存在且只存在(k)个数,这(k)个数的二进制以(y)的二进制为前缀,或者若(y)的二进制最低位为(0)时,(y)的最低位也可进(1),然后再以(y)为前缀。
不妨令(Judge(x))表示在([1,N])内以(x)的二进制为前缀的数的数量,即出现过(x)的互异的(path_j)的数量。那么我们从(x=1)开始,不断地将(x)左移,直到(Judge(x) < K),即可获得(Ans)的二进制最大长度。然后我们在这个(x)的基础上,从(x)的二进制高位到低位不断地尝试把(0)变成(1),若改变后(Judge(x))仍大于(K),则保持改变,否则继续判断下一位。
对于函数(Judge(x)),我们按在(x)后添加的二进制位数分开计算,假设现在我们要在(x)后添加一个长为(Len)的后缀,我们可以用二分算出长为(Len)的最大的后缀,即为在(x)后添加长度为(Len)的后缀时,([1,N])中前缀为(x)的数的数量。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
#define RG register int
#define LL long long
LL N,K;
inline LL f(LL x,LL Len){
LL L=0,R,Res=0;
if(x&1) R=(1LL<<Len)-1;
else R=(1LL<<(Len+1))-1;
x<<=Len;
if(x>N) return 0;
while(L<=R){
LL mid=(L+R)>>1;
if(x+mid<=N){Res=mid+1;L=mid+1;}
else R=mid-1;
}
return Res;
}
bool Judge(LL x){
LL temp=x,Res=0,Len=0;
while(temp<=N){Res+=f(x,Len);++Len;temp<<=1;}
if(Res>=K) return true;
return false;
}
inline LL GetLen(LL x){
LL Len=0;
while(x){++Len;x>>=1;}
return Len-1LL;
}
int main(){
cin>>N>>K;
LL Ans=1;
while(Judge(Ans<<1)) Ans<<=1;
LL Len=GetLen(Ans);
for(RG i=Len-1;i>=0;--i)
if(Judge(Ans+(1LL<<i))) Ans+=(1LL<<i);
cout<<Ans<<endl;
return 0;
}