版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目描述:
给定整数 m,k,求出正整数 n 使得 n+1,n+2,…,2n 中恰好有 m 个数 在二进制下恰好有 k 个 1。有多组数据。
输入:
第一行一个整数 t 表示数据组数。接下来 t 行每行两个整数 m,k。
输出:
每组数据输出一行两个整数,分别表示满足条件的n的最小值和最大值(无穷多此行输出一个数-1)。保证 10^18 以内存在满足条件的 n。
t<=2000,0<=m<=10^18,1<=k<=64。
题目分析:
令
表示
的二进制表示中1的数量,设
为
到
中
的数量。
因为
,所以有
(分
的末尾为0或1讨论)
题目即求满足
的
.
比较容易看出
.
因为 是随 单调不降的,所以题目即求满足 的 的上下界,二分答案然后用数位DP检验即可。
由于此题k<=64,当m=0时n的上界为 ,注意二分的范围。
Code:
#include<bits/stdc++.h>
#define LL unsigned long long
using namespace std;
int T,dig[65];
LL m,k,f[65][65];
LL dfs(int len,int s,bool fp){
if(!len) return s==k;
if(!fp&&~f[len][s]) return f[len][s];
LL ret=0;int mx=fp?dig[len]:1;
for(int i=0;i<=mx;i++) ret+=dfs(len-1,s+i,fp&&i==mx);
if(!fp) f[len][s]=ret;
return ret;
}
LL solve(LL n){
int len=0;
while(n) dig[++len]=n&1,n>>=1;
return dfs(len,0,1);
}
int main()
{
scanf("%d",&T);
while(T--){
memset(f,-1,sizeof f);
scanf("%lld%d",&m,&k);
if(k==1) {puts("-1");continue;}
k--;
LL l=1,r=1ull<<63,mid,L,R;
while(l<r){
mid=(l+r)>>1;
if(solve(mid-1)<m) l=mid+1;
else r=mid;
}
L=l,l=1,r=1ull<<63;
while(l<r){
mid=(l+r)>>1;
if(solve(mid-1)<=m) l=mid+1;
else r=mid;
}
R=l-1;
printf("%llu %llu\n",L,R);
}
}