[二分] Codeforces 1271E Common Number

题目链接

题解

容易发现,对于给定的\(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;
}

猜你喜欢

转载自www.cnblogs.com/AEMShana/p/12431440.html