zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    本来一行可以代替的树节点搜索
    mssql 重新开始标识
    TabContainer实现服务器端回传
    CSS中图片路径的问题
    Javascript在IE下设置innerHTML时出现"未知的运行时错误"
    sql union和union all的用法及效率
    关于动态添加TabPanel遇到的问题以及思考
    关于linq to sql调用存储过程,出现"无法枚举查询结果多次"的问题
    SQL Server 2005连接服务器时的26号错误解决!
    SQL 2000和2005 获取两表的差集
  • 原文地址:https://www.cnblogs.com/gj-Acit/p/7850337.html
Copyright © 2011-2022 走看看