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