题意:
求一个n,使得n+1到2n这些数的二进制中恰好有k个1的数有m个。
思路:
在k同样的情况下,打表之后发现单调性,能够二分。
问题转化为n+1到2n二进制表示之后1的个数为k的有多少个?
进一步统一,假设知道区间[0,x]内的二进制表示之后1的个数为k有cal(x)个,那么答案就是cal(2n)-cal(n)了。
如今要 求cal(x)。
假设x为 101101 。k=3;
由于求比小于等于x的数二进制表示之后1的个数为k的有多少个,所以当第一位为0的时候。后面5位中须要3位为0才干满足条件,C[5][3]。前两位同样,第三位为0的时候。前面已经有2个1。后面三位仅仅需1个1就可以,C[3][1]...
从样例中你应该能得出结论了吧。
逐位枚举。假设前d为同样,已有num个1,该位为1。我们能够将该位置0(由于求的是[0,x]内的二进制表示之后1的个数为k有多少个),那么后面还须要k-num个1,后面还有i为的话,那么ans+= C[i][k-num]。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 15
#define MAXN 100005
#define OO (1LL<<35)-1
#define mod 1000000007
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
typedef long long ll;
using namespace std;
ll n,m,k,d,ans,tot,flag,cnt;
ll C[70][70];
ll cal(ll x)
{
ll i,j,res=0,num=0;
for(i=62;i>=0;i--)
{
if(x&(1LL<<i))
{
if(k-num>=0) res+=C[i][k-num];
num++;
}
}
return res;
}
int main()
{
ll i,j,t,le,ri,mid,cnt;
for(i=0;i<=64;i++) C[i][0]=1;
for(i=1;i<=64;i++)
{
for(j=1;j<=i;j++)
{
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
while(cin>>m>>k)
{
le=1; ri=1LL<<62;
while(le<=ri)
{
mid=(le+ri)>>1;
cnt=cal(2*mid)-cal(mid);
if(m<=cnt)
{
ans=mid;
ri=mid-1;
}
else le=mid+1;
}
cout<<ans<<endl;
}
return 0;
}