Timus 1057. Amount of degrees
2008-08-01 21:51
190 查看
Timus 1057. Amount of degrees 要求计算指定范围内能够由 K 个不同的 B 的幂次之和组成的整数的个数。
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.
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 秒。
1057. Amount of degrees
Time Limit: 1.0 secondMemory 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
input | output |
---|---|
15 20 2 2 | 3 |
解答如下:
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 秒。
相关文章推荐
- Ural(Timus) 1146. Maximum Sum
- Ural(Timus) 1003 Parity
- ural(Timus) 1136. Parliament
- Timus 1017. The Staircases(转)
- timus 1711.Code Names
- Timus 1146. Maximum Sum
- Timus 1183. Brackets Sequence
- Timus 1029. Ministry
- Timus1132(二次剩余方程求解)
- timus1982. Electrification Plan---最小生成树
- Timus 1346. Intervals of Monotonicity
- Timus 1013 DP
- Timus 1880. Psych Up's Eigenvalues题解
- Timus 1149. Sinus Dances 打印复杂公式
- Timus 1712. Cipher Grille 题解
- Timus 1601. AntiCAPS 修正大写句子
- Timus 1104. Don’t Ask Woman about Her Age题解
- timus 2031. Overturned Numbers
- timus 1204. Idempotents【思路】
- Timus Online Judge1009---K-based Numbers(简单递推dp)