Description
给定 (mleq 10^18,kleq 64),求出一个正整数 (n) 使得 (n+1 sim 2n) 中恰好有 (m) 个数恰好有 (k) 位 (1),以及有多少个这样的数。
Solution
考场上根本没什么思路。
所以首先应该打个表,然后观察到 (f(i,k)) 表示 (i+1 sim 2i) 中恰有 (k) 个 (1) 的数的个数是有单调性的,实际上也容易证明:
[f(i+1,k)-f(i,k)=[popcount(2i+1)=k]+[popcount(2i+2)=k]-[popcount(i+1)=k]
]
容易发现 (2i+2) 和 (i+1) 的 (popcount) 总是相等的,所以一定有 (f(i,k)) 关于 (i) 是单调不降的。那么就可以二分出一个 (i) 的上下界。
(f(i,k)) 的值可以直接数位 dp 求出,注意特判 (k=1) 有无限种 (i) 的情况。
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
inline ll read(){
ll x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
bool a[64];
int K,tp=0;
ll dp[64][64];
ll dfs(int st,int k,bool lim){
if(st==63) return !k;
if(!lim){
ll &ret=dp[st][k];
if(~ret) return ret; else ret=0;
for(int i=0;i<2;i++)
ret+=dfs(st+1,k-i,0);
return ret;
}else{
ll ret=0;
for(int i=0;i<=a[st];i++)
ret+=dfs(st+1,k-i,i==a[st]);
return ret;
}
}
ll check(ll x){
for(int i=0;i<63;i++) a[i]=(x>>(62-i))&1;
reverse(a+1,a+1+tp); return dfs(1,K,1);
}
int main(){
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
for(int i=0;i<64;i++)
for(int j=0;j<64;j++) dp[i][j]=-1;
int T=read();
while(T--){
ll m=read(); K=read();
if(K==1)
printf("2 -1
");
else{
ll l=1,r=2e18,L=2e18;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid<<1)-check(mid)>=m)
L=mid,r=mid-1;
else l=mid+1;
}
l=1,r=2e18; ll R=1;
while(l<=r){
ll mid=(l+r)>>1;
if(check(mid<<1)-check(mid)<=m)
R=mid,l=mid+1;
else r=mid-1;
}
printf("%lld %lld
",L,R-L+1);
}
}
}