题目大意:
休息的时候,可以放松放松浑身的肌肉,打扫打扫卫生,感觉很舒服。在某一天,某LMZ 开始整理他那书架。已知他的书有n 本,从左到右按顺序排列。他想把书从矮到高排好序,而每一本书都有一个独一无二的高度Hi。他排序的方法是:每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。可是毕竟是休息时间,LMZ 不想花太多时间在给书排序这种事上面。因此他划分并翻转完第一次书之后,他想计算,他一共执行了多少次翻转操作才能把所有的书排好序。LMZ 惊奇地发现,第一次排序之前,他第一次划分出来的所有区间的长度都是偶数。
思路:
对于一个数列,我们可以先按照题目所说的将单调递减的数列取反。
- 5 3 2 1 6 4
就变成
- 1 2 3 5 4 6
这时候,每个曾经单调递减的数列就单调递增了,每个区间内的数列肯定满足单调递增,所以剩下的肯定是两个区间之间的数字不满足单调递增。
那么很容易发现,剩余需要取反的数字个数一定是现在数列逆序对的个数,所以,再用归并排序求出逆序对的个数即可。
代码:
#include <cstdio> #include <iostream> using namespace std; long long n,a[100011],b[100011],sum,i,j; void make(long long l,long long mid,long long r) //合并 { long long p1=l,p2=mid+1; for (long long i=l;i<=r;i++) { if (p1>mid) {b[i]=a[p2]; p2++;} else if (p2>r) {b[i]=a[p1]; p1++;} else if (a[p1]>a[p2]){sum+=1+r-p2; b[i]=a[p1]; p1++;} else {b[i]=a[p2]; p2++;} } for (long long i=l;i<=r;i++) //重新赋值 a[i]=b[i]; } void sorts(long long l,long long r) //拆分 { if (l<r) { long long mid=(l+r)/2; sorts(l,mid); sorts(mid+1,r); //拆成两半 make(l,mid,r); } } int main() { scanf("%lld",&n); for (i=1;i<=n;i++) scanf("%lld",&a[i]); i=1; j=1; while (i<n) //先进行一次 { while (a[j]>a[j+1]&&j<n) j++; if (j-i) { for (long long k=i;k<=i+(j-i+1)/2-1;k++) swap(a[k],a[j-k+i]); sum++; } i=j=j+1; } sorts(1,n); //求逆序对 printf("%lld\n",sum); return 0; }