您的位置:首页 > 其它

Timus 1057. Amount of degrees

2008-08-01 21:51 190 查看
Timus 1057. Amount of degrees 要求计算指定范围内能够由 K 个不同的 B 的幂次之和组成的整数的个数。

1057. Amount of degrees

Time Limit: 1.0 second

Memory Limit: 16 MB

Create a code to determine the amount of integers, lying in the set [X;Y] and being a sum of exactly K different integer degrees of B.

Example. Let X=15, Y=20, K=2, B=2. By this example 3 numbers are the sum of exactly two integer degrees of number 2:

17 = 24+20,

18 = 24+21,

20 = 24+22.

Input

The first line of input contains integers X and Y, separated with a space (1 ≤ X ≤ Y ≤ 231−1). The next two lines contain integers K and B (1 ≤ K ≤ 20; 2 ≤ B ≤ 10).

Output

Output should contain a single integer — the amount of integers, lying between X and Y, being a sum of exactly K different integer degrees of B.

Sample

inputoutput
15 20

2

2

3

Problem Source: Rybinsk State Avia Academy

解答如下:

1 using System;

2

3 namespace Skyiv.Ben.Timus

4 {

5 // http://acm.timus.ru/problem.aspx?space=1&num=1057
6 sealed class T1057

7 {

8 static void Main()

9 {

string[] ss = Console.ReadLine().Split();

uint x = uint.Parse(ss[0]);

uint y = uint.Parse(ss[1]);

int k = int.Parse(Console.ReadLine());

int b = int.Parse(Console.ReadLine());

int[] maxs = { 16, 17, 17, 18, 19, 21, 22, 24, 27, 30 };

int max = (20 - k < maxs.Length) ? maxs[20 - k] : 32;

uint[,] a = new uint[k + 1, max];

for (int i = 0; i <= k; i++) a[i, 0] = 1;

for (int j = 0; j < max; j++) a[0, j] = 1;

for (int j = 1; j < max; j++)

for (int i = 1; i <= k; i++)

a[i, j] = a[i, j - 1] + a[i - 1, j];

uint high = BinarySearch(y + 1, int.MaxValue, k, b, a);

Console.WriteLine(high - BinarySearch(x, high, k, b, a));

}

static uint BinarySearch(uint z, uint high, int k, int b, uint[,] a)

{

uint low = 1, mid = 1, z2 = 0;

while (low <= high)

{

mid = (low + high) / 2;

z2 = GetNth(mid, k, b, a);

if (z2 < z) low = mid + 1;

else if (z2 > z) high = mid - 1;

else return mid;

}

return mid + ((z2 < z) ? 1u : 0);

}

static uint GetNth(uint n, int k, int b, uint[,] a)

{

bool[] bs = GetNth(new bool[32], a.GetLength(1) - 1, n, k, a);

if (bs == null) return uint.MaxValue;

int bit = bs.Length - 1;

while (!bs[bit]) bit--;

long v = 0, b2 = 1;

for (int i = 0; i <= bit; i++, b2 *= b)

{

if (bs[i]) v += b2;

if (b2 > int.MaxValue || v > int.MaxValue) return uint.MaxValue;

}

return (uint)v;

}

static bool[] GetNth(bool[] bs, int m, uint n, int k, uint[,] a)

{

int bit = Seek(a, k, m, n);

if (bit + k >= bs.Length) return null;

bs[bit + k] = true;

if (bit >= 0)

{

if (n > a[k, bit] + a[k - 1, bit]) GetNth(bs, bit, n - a[k, bit], k - 1, a);

else

{

GetNth(bs, bit, n - a[k - 1, bit], k, a);

bs[bit + k - 1] = false;

}

}

else for (int i = 0; i < k - 1; i++) bs[i] = true; // n == 1

return bs;

}

static int Seek(uint[,] a, int k, int m, uint n)

{

for (int i = m; i >= 0; i--) if (a[k, i] < n) return i;

return -1;

}

}

}



这道题要求计算指定范围内 (从 X 到 Y,1 ≤ X ≤ Y ≤ 231−1) 能够由 K (1 ≤ K ≤ 20) 个不同的 B (2 ≤ B ≤ 10) 的幂次之和组成的整数的个数。

这个程序的关键在于第 41 到 54 行的 GetNth 方法,该方法返回满足以下条件的第 N 个 B 进制整数:该整数中有 K 个 1,其余数字全部都是 0。该方法在第 43 行调用第 56 到 72 行的 GetNth 方法,获得表示该整数的 0 和 1 的布尔数组。然后在第 44 到 53 行根据该布尔数组计算出该 B 进制整数。

而第 56 到 72 行的 GetNth 方法是根据一定的规律计算出所需的布尔数组。该方法在第 58 行调用第 74 到 78 行的 Seek 方法获得最高位的 1 的位置,在第 60 行设置最高位的 1。然后递归调用自身以设置其余的 1,这分为两种情况。第一种情况如图中浅黄色方块所示,对应程序第 63 行,第二种情况如图中浅青色方块所示,对应程序中第 66 到 67 行。如果递归到最后达到 N = 1 的情况,则对应程序中第 70 行。

而程序中第 74 到 78 行的 Seek 方法中使用的二维数组 A 是在主程序中的第 15 到 22 行初始化的。该数组由以下递推公式决定:

A(K, 0) = A(0, N) = 1

A(K, N) = A(K, N-1) + A(K-1, N)

其中 A(K, N-1) 对应前面提到的第二种情况,A(K-1, N) 对应前面提到的第一种情况。

最后,在主程序的第 23 到 24 行调用第 27 到 39 行的 BinarySearch 方法(该方法在第 33 行调用第 41 到 54 行的 GetNth 方法),使用二分搜索法分别获得最小的 N 使得第 N 个满足条件的 B 进制整数不小于 Y+1 以及不小于 X。最终答案就是这两者之差。

这个程序使用的算法虽然比较复杂,但应该是最优的。这个程序的实际运行时间是 0.078 秒。如果使用蛮力搜索法穷举每种可能,运行时间应该会远远就超出题目限制的 1.0 秒。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: