题意:
求一个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;
}