版权声明:我这种蒟蒻的文章,真正的大佬一般看不上的_(:з」∠)_ https://blog.csdn.net/Paulliant/article/details/82850301
题意
给定
和
,求一个
使得
范围内的数中二进制恰好有
个
的数,恰有
个。
思路
有一个“显然”的单调性,
越大,
中的数含有
个
的数单调不减,无论
的取值。
其实也不难证,当
变为
时,区间从
挪到
时,我们失去了
,获得了
,而
和
的二进制含有的
数相同,而多的那个
又不会让含有
个
的数变少,由此得出单调性。
那就直接二分
,用数位
验证答案即可。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=(y);++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=(y);--i)
typedef long long LL;
using namespace std;
LL dp[70][70],m;
int num[70],K;
LL dfs(int k,int status,bool ismax)
{
if(k==0)return status==K;
if(~dp[k][status]&&!ismax)return dp[k][status];
int maxer=ismax?num[k]:1;LL res=0;
FOR(i,0,maxer)res+=dfs(k-1,status+i,ismax&&i==maxer);
if(!ismax)dp[k][status]=res;
return res;
}
LL solve(LL k)
{
int n=0;
while(k)
{
num[++n]=k%2;
k>>=1;
}
return dfs(n,0,1);
}
int main()
{
scanf("%lld%d",&m,&K);
memset(dp,-1,sizeof(dp));
LL L=1,R=1e18;
while(L<R)
{
LL mid=L+R>>1;
if(solve(mid*2)-solve(mid)>=m)
{
R=mid;
}
else L=mid+1;
}
printf("%lld\n",L);
return 0;
}