zoukankan      html  css  js  c++  java
  • 休息

    【问题描述】
    休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某LMZ 开始整理他那书架。已知他的书有n 本,从左到右按顺序排列。他想把书从矮到高排好序,而每一本书都有一个独一无二的高度Hi。他排序的方法是:每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。可是毕竟是休息时间,LMZ 不想花太多时间在给书排序这种事上面。因此他划分并翻转完第一次书之后,他想计算,他一共执行了多少次翻转操作才能把所有的书排好序。LMZ 惊奇地发现,第一次排序之前,他第一次划分出来的所有区间的长度都是偶数。


    【输入格式】rest.in
    第一行一个正整数n, 为书的总数。
    接下来n 行,每行仅一个正整数Hi,为第i 本书的高度。


    【输出格式】rest.out
    仅一个整数,为LMZ 需要做的翻转操作的次数。
    样例输入  
    6
    5 3 2 1 6 4

    样例输出
    3


    【样例解释】
    第一次划分之后,翻转(5,3,2,1),(6,4)。之后,书的高度为1 2 3 5 4 6,
    然后便是翻转(5,4)即可。


    【数据范围与约定】
    对于10%的数据:n<=50
    对于40%的数据:n<=3000
    对于100%的数据:1<=n<=100000, 1<=Hi<=n

     

    首先题中说了第一次找到的序列长度都是偶数,这说明什么呢?那就是第一次调换后,下一次找到的递减区间一定是位于第一次的两个区间之间,且长度一定是2,因为别的地方经过调换后是递增的。举个列子例子:6 3 5 4 2 1,则(6, 3), (5,4,2,1),调换后就是3,6,1,2,4,5,,那么下一个区间要么是(6,1),要么不存在(排好序了)。

    接下来怎么做呢?

    因为只有长度为2的递减区间才去交换,所以就说明了只有相邻的逆袭对才去交换,那也就是说这道题就是让你求逆序对个数。

    逆序对这里用树状数组存。

     1 #include<cstdio>
     2 #include<iostream> 
     3 #include<cstring> 
     4 #include<cmath>
     5 #include<algorithm> 
     6 #include<vector>
     7 #include<queue>
     8 using namespace std;
     9 typedef long long ll;
    10 const int maxn = 1e5 + 5;
    11 int a[maxn], n;
    12 ll ans = 0;
    13 void Swap(int L, int R)
    14 {
    15     while(L < R)
    16     {
    17         swap(a[L], a[R]);
    18         L++; R--;
    19     }
    20 }
    21 void init()            //手动模拟第一次翻转 
    22 {
    23     int Sta = 1, End = 1;
    24     for(int i = 1; i <= n; ++i)
    25     {
    26         if(i < n && a[i + 1] < a[i]) End++;
    27         else 
    28         {
    29             if(Sta < End) {Swap(Sta, End); ans++;} 
    30             Sta = End = i + 1; 
    31         }
    32     }
    33 }
    34 int c[maxn];
    35 int lowbit(int x)        //树状数组板子 
    36 {
    37     return x & -x;
    38 }
    39 void add(int x, int d)
    40 {
    41     int pos = x;
    42     while(pos <= n)
    43     {
    44         c[pos] += d;
    45         pos += lowbit(pos);
    46     }
    47 }
    48 int query(int x)
    49 {
    50     int pos = x, ret = 0;
    51     while(pos > 0)
    52     {
    53         ret += c[pos];
    54         pos -= lowbit(pos);
    55     }
    56     return ret;
    57 }
    58 int main()
    59 {
    60     freopen("rest.in", "r", stdin);
    61     freopen("rest.out", "w", stdout);
    62     scanf("%d", &n);
    63     for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    64     init();
    65     for(int i = n; i > 0; --i)
    66     {
    67         ans += query(a[i]);
    68         add(a[i], 1);
    69     }
    70     printf("%lld
    ", ans);
    71     return 0;
    72 }

    这里我要说一下第一次手动模拟的交换的复杂度,刚开始我一直以为是O(n ^ 2),然后觉得可定能超时。结果竟A了。然后经过谨慎的思考,发现就是O(n)的。

    为什么呢?首先对于外层的循环,保证sta,end一定不会相交,所以外层就是O(n)的。而内层的交换操作,是在每一次sta,end区间内进行(end - sta + 2) / 2次。因为这个区间两两不相交,所以这个交换的复杂度不受外层循环的影响,O(n / 2),即O(n)。综上,复杂度为O(n)。 

  • 相关阅读:
    分治策略
    uva 11424
    lightoj 1214
    lightoj 1282 && uva 11029
    lightoj 1341
    zoj 2369 Two Cylinders
    几种素数筛法
    lightoj 1245
    hdoj 1299 Diophantus of Alexandria
    求前n项正整数的倒数和
  • 原文地址:https://www.cnblogs.com/mrclr/p/8945835.html
Copyright © 2011-2022 走看看