题目大意
求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
17=24+20
18=24+21
20=24+22
输入:第一行包含两个整数X 和Y。接下来两行包含整数K 和B。
输出:只包含一个整数,表示满足条件的数的个数。
数据规模:
1≤X≤Y≤231−1,1≤K≤20,2≤B≤10
。
解题思路
所求的数为互不相等的幂之和,亦即其B进制表示的各位数字都只能是0和1。我们只需讨论二进制的情况,然后其他进制可以转换成二进制来求解。
首先注意到本题具备区间减法性,即count(i…j) = count(0…j)-count(0…i-1),那么接下来的研究重点就是如何求0~n之间有多少符合要求的数。
假设n有i位,它的二进制形式是
(ai−1ai−2...a1a0)2
。我们从高位向低位扫描它,当某一位
aj
是0时则继续向下看,当
aj
为1时计数。计数方法如下:
- 遍历过程中要维护一个值tot,记录目前为止遍历到几个1
- 看到 aj 为1后,给ans累加上 (k−totj) 。它的意义是:把 aj 置0,然后右边j位选k-tot个变为1,这样的数显然满足“小于等于n且含k个1”。最后把tot++。
剩下的工作就是预处理组合数F(n,m)了,二进制的情形讨论完毕。接下来讨论其他进制与二进制的转换。
其他进制与二进制的一个明显区别是该进制下的X和Y某一位可能大于1,我们必须把它变成全01的形式,然后套用二进制情形的解法。
- 转换方法一
把X转化为“大于等于X且B进制下每位都是0或1且最接近X的数”,把Y转化为“小于等于Y且B进制下每位都是0或1且最接近Y的数”。设它们分别是nx和ny,如果我们已经实现了二进制情形的统计函数f(x,y,k),那么f(nx,ny,k)就是B进制下的答案。 - 转换方法二
把X和Y都转化为“小于等于它且B进制下每位都是0或1且最接近它的数”,这样只需要写一个转化函数。不过这种情况下求得f(nx,ny,k)后要特判一种情况:如果 (X)B 含大于1的位而 (nx)2 又恰好含k个1,这时候答案会多一,因此要把ans-1。
代码
#include <cstdio>
int F[33][33];
void init()
{
F[0][0] = 1;
for(int i=1; i<=30; i++)
{
F[i][0] = F[i-1][0];
for(int j=1; j<=i; j++)
F[i][j] = F[i-1][j] + F[i-1][j-1];
}
}
int calculate(int n, int k)
{
int ans = 0, tot = 0;
if(n == 0) return 0;
for(int i=30; i>=0; i--)
{
if(n & (1<<i))
{
ans += F[i][k-tot];
tot ++;
}
if(tot > k) break;
}
if(tot == k) ans ++;
return ans;
}
// 求B进制形式下,所有小于n且所有位都为0或1中的最大数
int change(int n, int B)
{
int num[33], top = -1;
int p, ans;
while(n)
{
num[++top] = n % B;
n /= B;
}
// 在n的B进制串中从高到低找到第一个不为0、1的位
// 把它置为1,然后后面全为1
for(p=top; p>=0 && num[p]<=1; p--);
ans = 0;
for(int j=top; j>p; j--)
if(num[j])
ans += (1<<j);
if(p >= 0)
ans += (1<<(p+1))-1;
return ans;
}
bool special(int x, int nx, int B, int k)
{
/// nx = change(x, B)
bool flag = true; // x的B进制串中只含01
while(x)
{
if(x % B > 1)
{
flag = false;
break;
}
x /= B;
}
int tot = 0; // 统计x二进制串中1的个数
while(nx)
{
if(nx & 1) tot ++;
nx >>= 1;
}
if(tot == k && !flag)
return true;
return false;
}
int main()
{
int x,y,k,B, ans;
init(); // 预处理组合数
while(scanf("%d%d", &x,&y) != EOF)
{
scanf("%d%d", &k,&B);
if(B > 2)
{
int nx = change(x, B);
int ny = change(y, B);
ans = calculate(ny, k) - calculate(nx-1, k);
// 特判一种情况
if(special(x, nx, B, k))
ans --;
}
else ans = calculate(y, k) - calculate(x-1, k);
printf("%d\n", ans);
}
return 0;
}