number
题目描述
给定整数 (m,k),求出正整数 (n) 使得 (n+1,n+2,…,2n) 中恰好有 (m) 个数在二进制下恰好有 (k) 个 (1)。 有多组数据。
输入数据
第一行一个整数 (t) 表示数据组数。接下来 (t) 行每行两个整数 (m),(k)。
输出数据
每组数据输出一行两个整数,第一个数表示 (long long) 范围内任意一个满足条件的 (n),第二个数表示满足条件的 (n) 的个数(无穷多用(-1)表示)。 保证 (10^{18}) 以内存在满足条件的 (n)。
如果每组数据第一个数全部正确,得 (4) 分。
如果每组数据第二个数全部正确,得 (6) 分。
数据范围
对于 (10\%) 的数据, (k=2)。
对于 (20\%) 的数据, (k<=3)。
对于另外 (50\%) 的数据, 保证满足条件的 (n) 均在 (10^{18}) 以内。
对于 (100\%) 的数据, (t<=2000), $0<=m<=10^{18}, (1<=k<=64)。
打表吧。
然后发现(k)一定时,(m)随(n)增大在整数域上连续增大。
进一步发现,其实(m)变化时的(n)是二进制下(k-1)个(1)从小到大排序而成的
于是可以预处理组合数求一下啦
要特判(m=0)
Code:
#include <cstdio>
#define ll long long
ll C[70][70];
void init()
{
C[0][0]=1;
for(int i=1;i<=67;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
}
int t;
ll cal(int k,ll m)
{
if(m==-1) return 0;
ll ans=0;
while(k)
{
int pos=k;
while(C[pos][k]<=m) ++pos;
--pos;
ans|=1ll<<pos;
m-=C[pos][k];
k--;
}
return ans;
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
ll m;int k;
scanf("%lld%d",&m,&k);
--k;
if(!k)
printf("1 -1
");
else
{
ll r=cal(k,m),l=cal(k,m-1);
printf("%lld %lld
",l+1ll,r-l);
}
}
return 0;
}
2018.10.13