休息

【问题描述】
休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某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\n", 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)。 

posted @ 2018-04-25 17:41  mrclr  阅读(202)  评论(0编辑  收藏  举报