題目連接:http://hihocoder.com/problemset/problem/1622?sid=1230113ios
若是一個區間[a, b]內剛好包含偶數個迴文整數,咱們就稱[a, b]是有趣的區間。 優化
例如[9, 12]包含兩個迴文整數9和11,因此[9, 12]是有趣的區間。[12, 20]包含0個迴文整數,因此[12, 20]也是有趣的。 spa
如今給定一個區間[a, b],請你求出[a, b]中全部知足a ≤ p ≤ q ≤ b的子區間[p, q]有多少個有趣的。code
第一行包含兩個整數a和b。 blog
對於30%的數據,1 ≤ a ≤ b ≤ 1000 排序
對於60%的數據,1 ≤ a ≤ b ≤ 100000 內存
對於100%的數據, 1 ≤ a ≤ b ≤ 1000000000ci
有趣的子區間數目get
10 20
46
菜ji題解,大牛請略過。。。string
剛看到題目是半點思路也沒有啊,要統計全部區間(10^9)^2,還要計算區間內迴文數的個數。這。。玩不了。(手動捂臉。。
可是仔細一想特麼即使區間大小長達1e9,可是1e9內的迴文數很少啊(由於前一半肯定了,後一半也就肯定了= = ),估算一下差很少在1e5個迴文數左右。。因而很瓜熟蒂落的預處理1e9內的全部迴文數並排序。。。(可是有什麼用呢= =)
若是每兩個迴文數當作一個區間,計算有趣區間個數。好比a, b, c, d, e五個迴文數(從小到大),那麼考慮左端點在(a, b]、右端點在(c, d]的全部區間都是有趣的,這樣的計算複雜度是O(1)的。[有趣區間數=(b - a) * (d - c)],這樣只要枚舉迴文數個數爲偶數的全部迴文數區間
好比加入上述例子中區間爲[l, r] 且 l < a, r > e, 那麼枚舉有趣的區間的:
1.左端點在[l, a],右端點在[b, c) // 區間內有兩個迴文數
2.左端點在[l, a],右端點在[d, e) // 區間內有四個迴文數
3.左端點在(a, b],右端點在[c, d) // ...
4.左端點在(a, b],右端點在[e, r] // ...
5.左端點在(b, c],右端點在[d, e) // ...
...
對於每一個枚舉都是保證區間內迴文數爲偶數的前提下,計算左端點可取的個數x(好比樣例1:x=a - l + 1), 右端點可取的個數y(好比樣例1:y=c - b),那麼這次枚舉的有趣的區間數爲x * y。最後對全部的x * y求和(即上面的枚舉狀況1.2.3.4.5....全部的x * y求和)便可。能夠看到這樣的複雜度爲O(1e5 ^ 2)=O(1e10),複雜度減小了很多,可是仍是接受不了啊= =
還要優化。。。
那。。繼續來。。。
相信細心的讀者已經注意到了,在上面的例子中狀況2和狀況5枚舉了同一個右邊界的狀況[d, e),緣由在於他們的左邊界[l, a]和(b, c]這兩個區間之間始終差距兩個(偶數個)迴文數,而狀況1中右邊界爲[b,c),與狀況5的左邊界一致,那麼在計算狀況一、二、5時,即可以統一處理:
首先計算區間[b, c)和區間[d, e)的長度和(差距爲偶數的區間長度和):
sum = (c - b) + (e - d) // + (g - f) + ...
那麼狀況1和狀況2能夠合併爲
ans += (a - l + 1) * sum (其實這個表明全部有趣的區間的左端點落在[l, a]的方法)
在計算狀況5時:
首先令sum -= c - b
ans += (c - b) * sum(其實這個表明全部有趣的區間的左端點落在(b, c]的方法)
好啦,複雜度順利降到了O(1e5)
至於代碼什麼的,個人一貫可讀性不高啦。。有了思路應該均可以搞得定~(只是大神分分鐘,蒟蒻我花了一整晚T_T)
順便說一句,個人代碼思路是[l, r]全部區間數減去非有趣的區間數計算的,由於不要忘了一個迴文數沒有的區間也是有趣的區間,也就是上面的左右端點都在[l, a)的區間也是有趣的。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <vector> 5 #include <queue> 6 #include <algorithm> 7 using namespace std; 8 9 typedef long long LL; 10 11 const int N = 1000005; 12 const int M = 1000000000; 13 14 int hwNums[N], cntHW; 15 16 int rever(int n) 17 { 18 int ans = 0; 19 while(n) 20 { 21 ans = ans * 10 + n % 10; 22 n /= 10; 23 } 24 return ans; 25 } 26 27 int getLenP(int n) 28 { 29 int ans = 1; 30 while(n) ans *= 10, n /= 10; 31 return ans; 32 } 33 34 void init() 35 { 36 for(int i = 1; i < 10000; i ++) 37 { 38 if(i < 10) hwNums[cntHW ++] = i; 39 40 int r = rever(i), p = getLenP(i); 41 hwNums[cntHW ++] = i * p + r; 42 43 for (int j = 0; j < 10; j ++) 44 { 45 LL num = (LL)i * 10 * p + j * p + r; 46 if(num <= M) hwNums[cntHW ++] = num; 47 } 48 } 49 // 隨意添加兩個更大的數,能夠略去後面的邊界狀況討論 50 hwNums[cntHW ++] = 1000000001; 51 hwNums[cntHW ++] = 1100000011; 52 sort(hwNums, hwNums + cntHW); 53 } 54 55 // 對於l, hw1, hw2, hw3, ..., r 56 // 計算有趣的區間的左端點落在[l, hw1], (hw2, hw3], (hw4, hw5] ... 的全部的方法數 57 LL count_ans(int l, int r) 58 { 59 int id = 0, tid; 60 while(hwNums[id] <= l) id ++; // while中略去了id < cntHW,由於上面添加了兩個超出邊界的數 61 if(hwNums[id] > r) return 0; 62 tid = id; 63 64 LL sum = 0, pre = hwNums[id ++]; 65 while(hwNums[id] <= r) 66 { 67 sum += hwNums[id] - pre; 68 pre = hwNums[++ id]; 69 id ++; 70 } 71 if(pre <= r) sum += r + 1 - pre; 72 73 LL ans = 0; 74 pre = l; 75 while(hwNums[tid] <= r) 76 { 77 ans += (hwNums[tid] - pre) * sum; 78 sum -= hwNums[tid + 1] - hwNums[tid]; 79 pre = hwNums[++tid]; 80 tid ++; 81 } 82 return ans; 83 } 84 85 int main() 86 { 87 //freopen("in.txt", "r", stdin); 88 89 init(); 90 91 int a, b; 92 cin >> a >> b; 93 a --; 94 95 LL n = b - a, ans = n * (n + 1) / 2; 96 97 int id = 0; 98 while(hwNums[id] <= a) id ++; 99 100 ans -= count_ans(a, b) + count_ans(hwNums[id], b); 101 102 cout << ans << endl; 103 104 return 0; 105 }