Timus 1318. Logarithm 要求将一些 128-bit 数进行 XOR 运算后,取对数(只计算到整数部分),然后求它们的和。
1318. Logarithm
Time Limit: 1.0 second
Memory Limit: 16 MB
Given a set A of N unordered 128-bit numbers. You are to compute a value of the function
where Ak — the kth element of A, log10X — the integer part of the decimal logarithm of X. We’ll assume that log100 = 0.
Input
The first input line contains a number N <= 5000. In the following N lines there are 128-bit numbers Ak presented by sets of numbers (a1k, a2k, a3k, a4k), each of them lies in range from 0 to 232-1. The number Ak can be obtained from this set according to the formula
Ak = 296a1k + 264a2k + 232a3k + a4k.
Output
You are to output the value of the function for the given set.
Sample
| input | output |
2
0 0 0 2324
0 2332 0 0
|
44
|
Problem Author: Idea: Nikita Shamgunov, prepared by Nikita Shamgunov, Anton Botov
Problem Source: VIII Collegiate Students Urals Programming Contest. Yekaterinburg, March 11-16, 2004
解答如下:
1 using System;
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 UInt128[] numbers = UInt128.Read(Console.In);
12 int v = 0;
13 for (int i = 0; i < numbers.Length - 1; i++)
14 for (int j = i + 1; j < numbers.Length; j++)
15 v += UInt128.Log10(UInt128.Xor(numbers[i], numbers[j]));
16 Console.WriteLine(v * 2);
17 }
18 }
19
20 sealed class UInt128 : IComparable<UInt128>
21 {
22 static readonly int n = 4;
23 static readonly UInt128[] TenPowers;
24 uint[] uint32s;
25
26 static UInt128()
27 {
28 TenPowers = new UInt128[39]; // Pow(10, 39) > UInt128.MaxValue
29 BigInteger p32 = BigInteger.Pow(2, 32), p64 = p32 * p32, p96 = p64 * p32, k = 1;
30 for (int i = 0; i < TenPowers.Length; i++, k *= 10)
31 {
32 BigInteger r;
33 uint q0 = uint.Parse(BigInteger.DivRem(k, p96, out r).ToString());
34 uint q1 = uint.Parse(BigInteger.DivRem(r, p64, out r).ToString());
35 uint q2 = uint.Parse(BigInteger.DivRem(r, p32, out r).ToString());
36 TenPowers[i] = new UInt128(uint.Parse(r.ToString()), q2, q1, q0);
37 }
38 }
39
40 UInt128(params uint[] uint32s)
41 {
42 this.uint32s = new uint[n];
43 for (int i = uint32s.Length - 1; i >= 0; i--) this.uint32s[i] = uint32s[i];
44 }
45
46 static UInt128 FromInt32s(string s)
47 {
48 string[] ss = s.Split();
49 return new UInt128(uint.Parse(ss[3]), uint.Parse(ss[2]), uint.Parse(ss[1]), uint.Parse(ss[0]));
50 }
51
52 public int CompareTo(UInt128 other)
53 {
54 if (other == null) return 1;
55 if (uint32s[3] > other.uint32s[3]) return 1;
56 if (uint32s[3] < other.uint32s[3]) return -1;
57 if (uint32s[2] > other.uint32s[2]) return 1;
58 if (uint32s[2] < other.uint32s[2]) return -1;
59 if (uint32s[1] > other.uint32s[1]) return 1;
60 if (uint32s[1] < other.uint32s[1]) return -1;
61 if (uint32s[0] > other.uint32s[0]) return 1;
62 if (uint32s[0] < other.uint32s[0]) return -1;
63 return 0;
64 }
65
66 public static UInt128[] Read(TextReader reader)
67 {
68 UInt128[] numbers = new UInt128[int.Parse(reader.ReadLine())];
69 for (int i = numbers.Length - 1; i >= 0; i--) numbers[i] = UInt128.FromInt32s(reader.ReadLine());
70 return numbers;
71 }
72
73 public static UInt128 Xor(UInt128 x, UInt128 y)
74 {
75 UInt128 v = new UInt128();
76 for (int i = 0; i < n; i++) v.uint32s[i] = x.uint32s[i] ^ y.uint32s[i];
77 return v;
78 }
79
80 public static int Log10(UInt128 x)
81 {
82 int low = 0;
83 int high = TenPowers.Length - 1;
84 int mid = 0;
85 int cmp = 0;
86 while (low <= high)
87 {
88 mid = (low + high) / 2;
89 cmp = TenPowers[mid].CompareTo(x);
90 if (cmp < 0) low = mid + 1;
91 else if (cmp > 0) high = mid - 1;
92 else return mid;
93 }
94 return mid - ((cmp > 0) ? 1 : 0);
95 }
96 }
97 }
这个程序的算法是非常直接了当的。程序中用到了 BigInteger 类,其源代码在 浅谈 BigInteger 这篇随笔中。
因为 2128 ≈ 3.4 x 1038,所以在程序中使用一个数组来存储 100, 101, ... 1038 各个数(程序中第 28 行)。在计算对数时,即程序中第 80 行开始的 Log10 方法中,使用二分搜索法在该数组中查找就行了。
这道题目限时是 1.0 秒。问题是我的这个程序运行时会超时,无法 Accept。我想不出来还有什么更快的方法了。谁可以告诉我更好的算法?