1318. Logarithm
Time Limit: 1.0 second
Memory Limit: 16 MB
Input
Output
Sample
input | output |
---|---|
2 0 0 0 2324 0 2332 0 0 |
44 |
Problem Source: VIII Collegiate Students Urals Programming Contest. Yekaterinburg, March 11-16, 2004
解答如下(只保留 Main 方法):
{
UInt128[] numbers = UInt128.Read(Console.In);
int v = 0;
for (int i = 0; i < numbers.Length - 1; i++)
for (int j = i + 1; j < numbers.Length; j++)
v += UInt128.Log10(UInt128.Xor(numbers[i], numbers[j]));
Console.WriteLine(v * 2);
}
这个程序的算法是非常直接了当的。程序中用到了 BigInteger 类,其源代码在 浅谈 BigInteger 这篇随笔中。
因为 2128 ≈ 3.4 x 1038,所以在程序中使用一个数组来存储 100, 101, ... 1038 各个数(程序中第 28 行)。在计算对数时,即程序中第 80 行开始的 Log10 方法中,使用二分搜索法在该数组中查找就行了。
这道题目限时是 1.0 秒。问题是我的这个程序运行时会超时,无法 Accept。我想不出来还有什么更快的方法了。谁可以告诉我更好的算法?
(以下是 2009-03-17 补充的,并于 2009-03-19 修改)
根据本文5楼的评论,参阅了数学研发论坛上的“关于一个运算优化的问题”的贴子,终于把这题通过了。
根据本文12楼的评论,修改后的程序的运行时间是 0.218 秒。
首先,根据该贴子,我们有以下定理:
上面定理中的所有对数的值都要取整。实际上,log2X 就表示 X 的二进制位数。同样,log10X 就表示 X 的十进制位数。
利用上面的定理,就可以用分治法来解这道题。
还是先看看 C# 源程序吧:
2 using System.IO;
3
4 namespace Skyiv.Ben.Timus
5 {
6 // http://acm.timus.ru/problem.aspx?space=1&num=1318
7 sealed class T1318
8 {
9 static void Main()
10 {
11 var p = UInt128.Read(Console.In);
12 Array.Sort(p);
13 Console.WriteLine(2 * Solve(p, 127, 0, p.Length - 1));
14 }
15
16 static int Solve(UInt128[] p, int bit, int low, int high)
17 {
18 if (bit < 3 || low >= high) return 0;
19 var mid = BinarySearch(p, bit, low, high);
20 var v = Solve(p, bit, low, high, mid);
21 v += Solve(p, bit - 1, low, mid - 1);
22 v += Solve(p, bit - 1, mid, high);
23 return v;
24 }
25
26 static int Solve(UInt128[] p, int bit, int low, int high, int mid)
27 {
28 if (low >= mid || mid > high) return 0;
29 var n = UInt128.Log10s[bit];
30 if (n >= 0) return n * (mid - low) * (high - mid + 1);
31 return Solve(p, bit - 1, -n, low, mid - 1, mid, high);
32 }
33
34 static int Solve(UInt128[] p, int bit, int n, int low0, int high0, int low1, int high1)
35 {
36 if (bit < 3 || low0 > high0 || low1 > high1) return 0;
37 var mid0 = BinarySearch(p, bit, low0, high0);
38 var mid1 = BinarySearch(p, bit, low1, high1);
39 if (UInt128.Powers[n].IsSetBit(bit)) return
40 ((mid0 - low0) * (mid1 - low1) + (high0 + 1 - mid0) * (high1 + 1 - mid1)) * (n - 1) +
41 Solve(p, bit - 1, n, low0, mid0 - 1, mid1, high1) +
42 Solve(p, bit - 1, n, mid0, high0, low1, mid1 - 1);
43 else return
44 ((mid0 - low0) * (high1 + 1 - mid1) + (high0 + 1 - mid0) * (mid1 - low1)) * n +
45 Solve(p, bit - 1, n, low0, mid0 - 1, low1, mid1 - 1) +
46 Solve(p, bit - 1, n, mid0, high0, mid1, high1);
47 }
48
49 static int BinarySearch(UInt128[] p, int bit, int low, int high)
50 {
51 var isSet = false;
52 int mid = 0, i = low, j = high;
53 while (i <= j)
54 if (isSet = p[mid = (i + j) / 2].IsSetBit(bit)) j = mid - 1;
55 else i = mid + 1;
56 return isSet ? mid : (mid + 1);
57 }
58 }
59
60 sealed class UInt128 : IComparable<UInt128>
61 {
62 public static readonly UInt128[] Powers = new UInt128[39]; // 存放 10 的 0, 1, , 38 次幂
63 public static readonly int[] Log10s = new int[128]; // 值域为: [-38, 38]
64 uint[] data;
65
66 static UInt128()
67 {
68 var log2s = new int[Powers.Length];
69 BigInteger p32 = BigInteger.Pow(2, 32), p64 = p32 * p32, p96 = p64 * p32, q = 1;
70 for (var i = 0; i < Powers.Length; i++, q *= 10)
71 {
72 BigInteger r;
73 var u0 = uint.Parse(BigInteger.DivRem(q, p96, out r).ToString());
74 var u1 = uint.Parse(BigInteger.DivRem(r, p64, out r).ToString());
75 var u2 = uint.Parse(BigInteger.DivRem(r, p32, out r).ToString());
76 Powers[i] = new UInt128(uint.Parse(r.ToString()), u2, u1, u0);
77 log2s[i] = Powers[i].Log2();
78 }
79 for (int i = 1, j = 0; i < Log10s.Length; i++)
80 {
81 if (j < log2s.Length - 1 && i == log2s[j + 1]) j++;
82 Log10s[i] = (i == log2s[j]) ? -j : j;
83 }
84 }
85
86 UInt128(params uint[] data)
87 {
88 this.data = new uint[128 / 32];
89 for (int i = data.Length - 1; i >= 0; i--) this.data[i] = data[i];
90 }
91
92 static UInt128 FromInt32s(string s)
93 {
94 var u = s.Split();
95 return new UInt128(uint.Parse(u[3]), uint.Parse(u[2]), uint.Parse(u[1]), uint.Parse(u[0]));
96 }
97
98 public int CompareTo(UInt128 other)
99 {
100 if (data[3] > other.data[3]) return 1;
101 if (data[3] < other.data[3]) return -1;
102 if (data[2] > other.data[2]) return 1;
103 if (data[2] < other.data[2]) return -1;
104 if (data[1] > other.data[1]) return 1;
105 if (data[1] < other.data[1]) return -1;
106 if (data[0] > other.data[0]) return 1;
107 if (data[0] < other.data[0]) return -1;
108 return 0;
109 }
110
111 public static UInt128[] Read(TextReader reader)
112 {
113 var p = new UInt128[int.Parse(reader.ReadLine())];
114 for (var i = p.Length - 1; i >= 0; i--) p[i] = UInt128.FromInt32s(reader.ReadLine());
115 return p;
116 }
117
118 int Log2()
119 {
120 int[] bs = { 0, 32, 64, 96 };
121 for (var i = 3; i >= 0; i--)
122 {
123 var u = data[i];
124 if (u == 0) continue;
125 for (var bit = 31; bit >= 0; bit--, u <<= 1)
126 if ((~int.MaxValue & u) != 0) return bit + bs[i];
127 }
128 return -1;
129 }
130
131 public bool IsSetBit(int bit)
132 {
133 return ((data[bit / 32] >> (bit % 32)) & 1) != 0;
134 }
135 }
136 }
程序中,Main 方法位于第 9 到 14 行。第 11 行读取输入到一个 UInt128 数组中。第 12 行将该数组排序。第 13 行调用 Solve 方法进行分治法递归计算来解题。其中传入的参数 127 表示从 UInt128 的最高位开始,逐步往低位计算。
在第 16 到 24 行的 Solve 方法中,第 19 行的 mid 指示数组 p 的首个使第 bit 位为 1 的元素。这样,根据 mid 就把数组 p 的元素分为两组,第 20 行调用第二个 Solve 方法处理这两组之间的元素。第 21 和 22 行递归调用 Solve 自身分别处理这两组内的元素。
在第 26 到 32 行的第二个 Solve 方法中,如果发现这两组元素满足上面定理的条件,则在第 30 行直接将计算结果返回。否则,就在第 31 行调用第三个 Solve 方法来处理。
在第 34 到 47 行的第三个 Solve 方法中,继续根据低一位的第 bit 位再把这两组元素分别分为两组。这四组元素之间异或后的对数值不是 n 就是 n - 1。这时根据 10n 的二进制表示法中第 bit 位来判断是哪种情况。其中有两组可以直接得到结果,另外两组就递归地调用自身来处理。
实际上这整个程序都没有进行过 Xor 和 Log10 运算,而是不断通过分组进行处理。
第 60 到 136 行就是 UInt128 类。
第 62 行的 powers 静态数组存放 100, 101, ..., 1038。
第 63 行的 Log10s 静态数组存放的是 log102n,其中 n 从 0 到 127。这个数组的意思是指定的二进制位的数所对应的十进制数有几位,并且该位数第一个出现时对应的数组元素取负值。这个数组在程序的第 29 行用来判断是否满足上述定理的条件。
第 64 行的 data 数组使用 4 个 UInt32 来表示 UInt128。
第 66 到 84 行的静态构造函数用来初始化以上两个静态数组。
UInt128 类的其它方法也都很简单,就不多说了。
(以下是 2009-03-26 补充的)
这个程序还可以优化,即事先计算出静态数组 Powers 和 Log10s 的值,不使用 BigInteger 类。将递归中没有改变的参数 p 外提为类的字段。如下所示:
2 using System.IO;
3
4 namespace Skyiv.Ben.Timus
5 {
6 // http://acm.timus.ru/problem.aspx?space=1&num=1318
7 sealed class T1318
8 {
9 static UInt128[] p;
10 static int n;
11
12 static void Main()
13 {
14 Array.Sort(p = UInt128.Read(Console.In));
15 Console.WriteLine(2 * Solve(127, 0, p.Length - 1));
16 }
17
18 static int Solve(int bit, int low, int high)
19 {
20 if (bit < 3 || low >= high) return 0;
21 var mid = BinarySearch(bit, low, high);
22 return Solve(bit, low, high, mid) +
23 Solve(bit - 1, low, mid - 1) +
24 Solve(bit - 1, mid, high);
25 }
26
27 static int Solve(int bit, int low, int high, int mid)
28 {
29 if (low >= mid || mid > high) return 0;
30 n = UInt128.Log10s[bit];
31 if (n >= 0) return n * (mid - low) * (high - mid + 1);
32 n = -n;
33 return Solve(bit - 1, low, mid - 1, mid, high);
34 }
35
36 static int Solve(int bit, int low0, int high0, int low1, int high1)
37 {
38 if (bit < 3 || low0 > high0 || low1 > high1) return 0;
39 var mid0 = BinarySearch(bit, low0, high0);
40 var mid1 = BinarySearch(bit, low1, high1);
41 return UInt128.Powers[n].IsSetBit(bit) ?
42 (((mid0 - low0) * (mid1 - low1) + (high0 + 1 - mid0) * (high1 + 1 - mid1)) * (n - 1) +
43 Solve(bit - 1, low0, mid0 - 1, mid1, high1) +
44 Solve(bit - 1, mid0, high0, low1, mid1 - 1)) :
45 (((mid0 - low0) * (high1 + 1 - mid1) + (high0 + 1 - mid0) * (mid1 - low1)) * n +
46 Solve(bit - 1, low0, mid0 - 1, low1, mid1 - 1) +
47 Solve(bit - 1, mid0, high0, mid1, high1));
48 }
49
50 static int BinarySearch(int bit, int low, int high)
51 {
52 var isSet = false;
53 int mid = 0, i = low, j = high;
54 while (i <= j)
55 if (isSet = p[mid = (i + j) / 2].IsSetBit(bit)) j = mid - 1;
56 else i = mid + 1;
57 return isSet ? mid : (mid + 1);
58 }
59 }
60
61 sealed class UInt128 : IComparable<UInt128>
62 {
63 public static readonly UInt128[] Powers =
64 {
65 new UInt128(1, 0, 0, 0),
66 new UInt128(10, 0, 0, 0),
67 new UInt128(100, 0, 0, 0),
68 new UInt128(1000, 0, 0, 0),
69 new UInt128(10000, 0, 0, 0),
70 new UInt128(100000, 0, 0, 0),
71 new UInt128(1000000, 0, 0, 0),
72 new UInt128(10000000, 0, 0, 0),
73 new UInt128(100000000, 0, 0, 0),
74 new UInt128(1000000000, 0, 0, 0),
75 new UInt128(1410065408, 2, 0, 0),
76 new UInt128(1215752192, 23, 0, 0),
77 new UInt128(3567587328, 232, 0, 0),
78 new UInt128(1316134912, 2328, 0, 0),
79 new UInt128(276447232, 23283, 0, 0),
80 new UInt128(2764472320, 232830, 0, 0),
81 new UInt128(1874919424, 2328306, 0, 0),
82 new UInt128(1569325056, 23283064, 0, 0),
83 new UInt128(2808348672, 232830643, 0, 0),
84 new UInt128(2313682944, 2328306436, 0, 0),
85 new UInt128(1661992960, 1808227885, 5, 0),
86 new UInt128(3735027712, 902409669, 54, 0),
87 new UInt128(2990538752, 434162106, 542, 0),
88 new UInt128(4135583744, 46653770, 5421, 0),
89 new UInt128(2701131776, 466537709, 54210, 0),
90 new UInt128(1241513984, 370409800, 542101, 0),
91 new UInt128(3825205248, 3704098002, 5421010, 0),
92 new UInt128(3892314112, 2681241660, 54210108, 0),
93 new UInt128(268435456, 1042612833, 542101086, 0),
94 new UInt128(2684354560, 1836193738, 1126043566, 1),
95 new UInt128(1073741824, 1182068202, 2670501072, 12),
96 new UInt128(2147483648, 3230747430, 935206946, 126),
97 new UInt128(0, 2242703233, 762134875, 1262),
98 new UInt128(0, 952195850, 3326381459, 12621),
99 new UInt128(0, 932023908, 3199043520, 126217),
100 new UInt128(0, 730304488, 1925664130, 1262177),
101 new UInt128(0, 3008077584, 2076772117, 12621774),
102 new UInt128(0, 16004768, 3587851993, 126217744),
103 new UInt128(0, 160047680, 1518781562, 1262177448)
104 };
105 public static readonly int[] Log10s =
106 {
107 0, 0, 0, -1, 1, 1, -2, 2, 2, -3, 3, 3, 3, -4, 4, 4, -5, 5, 5, -6, 6, 6, 6, -7, 7, 7,
108 -8, 8, 8, -9, 9, 9, 9, -10, 10, 10, -11, 11, 11, -12, 12, 12, 12, -13, 13, 13,
109 -14, 14, 14, -15, 15, 15, 15, -16, 16, 16, -17, 17, 17, -18, 18, 18, 18,
110 -19, 19, 19, -20, 20, 20, -21, 21, 21, 21, -22, 22, 22, -23, 23, 23,
111 -24, 24, 24, 24, -25, 25, 25, -26, 26, 26, -27, 27, 27, 27, -28, 28, 28,
112 -29, 29, 29, -30, 30, 30, -31, 31, 31, 31, -32, 32, 32, -33, 33, 33,
113 -34, 34, 34, 34, -35, 35, 35, -36, 36, 36, -37, 37, 37, 37, -38, 38
114 };
115 uint[] data;
116
117 UInt128(params uint[] data)
118 {
119 this.data = new uint[128 / 32];
120 for (int i = data.Length - 1; i >= 0; i--) this.data[i] = data[i];
121 }
122
123 public int CompareTo(UInt128 other)
124 {
125 if (data[3] > other.data[3]) return 1;
126 if (data[3] < other.data[3]) return -1;
127 if (data[2] > other.data[2]) return 1;
128 if (data[2] < other.data[2]) return -1;
129 if (data[1] > other.data[1]) return 1;
130 if (data[1] < other.data[1]) return -1;
131 if (data[0] > other.data[0]) return 1;
132 if (data[0] < other.data[0]) return -1;
133 return 0;
134 }
135
136 public bool IsSetBit(int bit)
137 {
138 return ((data[bit / 32] >> (bit % 32)) & 1) != 0;
139 }
140
141 public static UInt128[] Read(TextReader reader)
142 {
143 var p = new UInt128[int.Parse(reader.ReadLine())];
144 for (var i = p.Length - 1; i >= 0; i--)
145 {
146 var u = reader.ReadLine().Split();
147 p[i] = new UInt128(uint.Parse(u[3]), uint.Parse(u[2]), uint.Parse(u[1]), uint.Parse(u[0]));
148 }
149 return p;
150 }
151 }
152 }
优化后程序的运行时间降低到 0.156 秒。
另外,按以下方法直接计算也是可以的,运行时间为 0.406 秒。
2 using System.IO;
3
4 namespace Skyiv.Ben.Timus
5 {
6 // http://acm.timus.ru/problem.aspx?space=1&num=1318
7 sealed class T1318b
8 {
9 static readonly int nUInt = 128 / 32;
10
11 static void Main()
12 {
13 var p = Read(Console.In);
14 Console.WriteLine(2 * Solve(p));
15 }
16
17 static uint[] Read(TextReader reader)
18 {
19 var n = int.Parse(reader.ReadLine());
20 var p = new uint[n * nUInt];
21 for (var i = 0; i < n; i++)
22 {
23 var ss = reader.ReadLine().Split();
24 for (var j = 0; j < nUInt; j++) p[i * nUInt + j] = uint.Parse(ss[j]);
25 }
26 return p;
27 }
28
29 static int Solve(uint[] p)
30 {
31 var v = 0;
32 for (var i = p.Length / nUInt - 1; i > 0; i--)
33 for (var j = i - 1; j >= 0; j--)
34 {
35 var u = i * nUInt;
36 var y = j * nUInt;
37 var a = p[u] ^ p[y];
38 var b = p[u + 1] ^ p[y + 1];
39 var c = p[u + 2] ^ p[y + 2];
40 var d = p[u + 3] ^ p[y + 3];
41 if (a == 0 && b == 0 && c == 0) goto label1;
42 if (a > 1262177448 || a == 1262177448 && (b > 1518781562 || b == 1518781562 && c >= 160047680)) { v += 38; continue; }
43 if (a > 126217744 || a == 126217744 && (b > 3587851993 || b == 3587851993 && c >= 16004768)) { v += 37; continue; }
44 if (a > 12621774 || a == 12621774 && (b > 2076772117 || b == 2076772117 && c >= 3008077584)) { v += 36; continue; }
45 if (a > 1262177 || a == 1262177 && (b > 1925664130 || b == 1925664130 && c >= 730304488)) { v += 35; continue; }
46 if (a > 126217 || a == 126217 && (b > 3199043520 || b == 3199043520 && c >= 932023908)) { v += 34; continue; }
47 if (a > 12621 || a == 12621 && (b > 3326381459 || b == 3326381459 && c >= 952195850)) { v += 33; continue; }
48 if (a > 1262 || a == 1262 && (b > 762134875 || b == 762134875 && c >= 2242703233)) { v += 32; continue; }
49 if (a > 126 || a == 126 && (b > 935206946 || b == 935206946 && (c > 3230747430 || c == 3230747430 && d > 2147483647))) { v += 31; continue; }
50 if (a > 12 || a == 12 && (b > 2670501072 || b == 2670501072 && (c > 1182068202 || c == 1182068202 && d > 1073741823))) { v += 30; continue; }
51 if (a > 1 || a == 1 && (b > 1126043566 || b == 1126043566 && (c > 1836193738 || c == 1836193738 && d > 2684354559))) { v += 29; continue; }
52 if (a > 0 || b > 542101086 || b == 542101086 && (c > 1042612833 || c == 1042612833 && d > 268435455)) { v += 28; continue; }
53 if (b > 54210108 || b == 54210108 && (c > 2681241660 || c == 2681241660 && d > 3892314111)) { v += 27; continue; }
54 if (b > 5421010 || b == 5421010 && (c > 3704098002 || c == 3704098002 && d > 3825205247)) { v += 26; continue; }
55 if (b > 542101 || b == 542101 && (c > 370409800 || c == 370409800 && d > 1241513983)) { v += 25; continue; }
56 if (b > 54210 || b == 54210 && (c > 466537709 || c == 466537709 && d > 2701131775)) { v += 24; continue; }
57 if (b > 5421 || b == 5421 && (c > 46653770 || c == 46653770 && d > 4135583743)) { v += 23; continue; }
58 if (b > 542 || b == 542 && (c > 434162106 || c == 434162106 && d > 2990538751)) { v += 22; continue; }
59 if (b > 54 || b == 54 && (c > 902409669 || c == 902409669 && d > 3735027711)) { v += 21; continue; }
60 if (b > 5 || b == 5 && (c > 1808227885 || c == 1808227885 && d > 1661992959)) { v += 20; continue; }
61 if (b > 0 || c > 2328306436 || c == 2328306436 && d > 2328306435) { v += 19; continue; }
62 if (c > 232830643 || c == 232830643 && d > 2808348671) { v += 18; continue; }
63 if (c > 23283064 || c == 23283064 && d > 1569325055) { v += 17; continue; }
64 if (c > 2328306 || c == 2328306 && d > 1874919423) { v += 16; continue; }
65 if (c > 232830 || c == 232830 && d > 2764472319) { v += 15; continue; }
66 if (c > 23283 || c == 23283 && d > 276447231) { v += 14; continue; }
67 if (c > 2328 || c == 2328 && d > 1316134911) { v += 13; continue; }
68 if (c > 232 || c == 232 && d > 3567587327) { v += 12; continue; }
69 if (c > 23 || c == 23 && d > 1215752191) { v += 11; continue; }
70 if (c > 2 || c == 2 && d > 1410065407) { v += 10; continue; }
71 label1:
72 if (c > 0 || d > 999999999) { v += 9; continue; }
73 if (d > 99999999) { v += 8; continue; }
74 if (d > 9999999) { v += 7; continue; }
75 if (d > 999999) { v += 6; continue; }
76 if (d > 99999) { v += 5; continue; }
77 if (d > 9999) { v += 4; continue; }
78 if (d > 999) { v += 3; continue; }
79 if (d > 99) { v += 2; continue; }
80 if (d > 9) { v ++; continue; }
81 }
82 return v;
83 }
84 }
85 }
返回目录