hiho1622 有趣的子区间(YY)

 

题目链接:http://hihocoder.com/problemset/problem/1622?sid=1230113

#1622 : 有趣的子区间

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

如果一个区间[a, b]内恰好包含偶数个回文整数,我们就称[a, b]是有趣的区间。  

例如[9, 12]包含两个回文整数9和11,所以[9, 12]是有趣的区间。[12, 20]包含0个回文整数,所以[12, 20]也是有趣的。  

现在给定一个区间[a, b],请你求出[a, b]中所有满足a ≤ p ≤ q ≤ b的子区间[p, q]有多少个有趣的。

输入

第一行包含两个整数a和b。  

对于30%的数据,1 ≤ a ≤ b ≤ 1000  

对于60%的数据,1 ≤ a ≤ b ≤ 100000  

对于100%的数据, 1 ≤ a ≤ b ≤ 1000000000

输出

有趣的子区间数目

样例输入
10 20
样例输出
46

 

菜ji题解,大牛请略过。。。

 

刚看到题目是半点思路也没有啊,要统计所有区间(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的左边界一致,那么在计算情况1、2、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 }

 

posted @ 2017-11-17 10:26  再见~雨泉  阅读(542)  评论(0编辑  收藏  举报