题意:
让你求出一个n,使得从n+1到2n之间有正好m个二进制位上为1的个数=k的数
3->11 两个1
题解:
这道题满足二分的性质,来证明一下:
假设当前的区间为n~2n,当n->n+1时,2n->2n+2,我们少了一个n+1,但是多了2n+1,和2n+2,2n+2与n+1的位数是相同的,相当于多了一个2n+1,那么n越大,满足条件的可能越多,这个曲线满足二分的性质,所以在二分的时候做数位DP即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[70][70];
int a[70];
ll m,k;
ll dfs(int pos,int num,int mx)
{
if(pos<0)
return num==k;
if(~dp[pos][num]&&!mx)
return dp[pos][num];
ll ans=0;
int top=mx?a[pos]:1;
if(num==k)
top=0;
for(int i=0;i<=top;i++)
ans+=dfs(pos-1,num+(i==1),mx&&i==top);
if(!mx)
dp[pos][num]=ans;
return ans;
}
int init(ll x)
{
ll ret=1;
int ans,cnt=0;
while(ret<=x)
ret*=2,cnt++;
ret/=2,cnt--;
ans=cnt;
while(ret)
{
a[cnt--]=(x&ret)?1:0;
ret/=2;
}
return ans;
}
int main()
{
scanf("%lld%lld",&m,&k);
ll low=1,high=1e18,mid;
while(high>=low)
{
mid=low+high>>1;
memset(dp,-1,sizeof(dp));
ll r=dfs(init(mid*2),0,1);
memset(dp,-1,sizeof(dp));
ll l=dfs(init(mid),0,1);
if(r-l>m)
high=mid-1;
else if(r-l<m)
low=mid+1;
else
break;
}
printf("%lld\n",mid);
return 0;
}