Description
休息的時候,可以放鬆放鬆渾身的肌肉,打掃打掃衛生,感覺很舒服。在某一天,某LMZ 開始整理他那書架。已知他的書有n 本,從左到右按順序排列。他想把書從矮到高排好序,而每一本書都有一個獨一無二的高度Hi。他排序的方法是:每一次將所有的書劃分爲儘量少的連續部分,使得每一部分的書的高度都是單調下降,然後將其中所有不少於2 本書的區間全部翻轉。重複執行以上操作,最後使得書的高度全部單調上升。可是畢竟是休息時間,LMZ 不想花太多時間在給書排序這種事上面。因此他劃分並翻轉完第一次書之後,他想計算,他一共執行了多少次翻轉操作才能把所有的書排好序。LMZ 驚奇地發現,第一次排序之前,他第一次劃分出來的所有區間的長度都是偶數。
Input
第一行一個正整數n, 爲書的總數。
接下來一行n個數,第i個正整數Hi,爲第i 本書的高度。
Output
僅一個整數,爲LMZ 需要做的翻轉操作的次數。
Sample Input
6
5 3 2 1 6 4
Sample Output
3
【樣例解釋】
第一次劃分之後,翻轉(5,3,2,1),(6,4)。之後,書的高度爲1 2 3 5 4 6,然後便是翻轉(5,4)即可。
Data Constraint
對於10%的數據:n<=50
對於40%的數據:n<=3000
對於100%的數據:1<=n<=100000, 1<=Hi<=n
//written by zzy
題目大意:
對一個序列每個連續的最長下降子序列進行翻轉操作,求到升序列的次數。
題解:
第一次劃分翻轉後,數列的所有下降子序列必然長度爲2,此時翻轉只能兩兩翻轉,次數等於逆序對數,用歸併排序求即可,
Ps:
歸併排序爲什麼可以求逆序對數?
降整個區間分成若干個沒有交集的小區間,分別求出每個區間的逆序對數併合並排序成個大區間,不會
影響到與其他區間的逆序對數。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 100005
using namespace std;
int i,j,n,m,p;
long long ans;
int a[N],c[N];
void merge_sort(int l,int r)
{
if (l==r) return;
int mid=(l+r)/2,i=l,j=mid+1,k=i;
merge_sort(l,mid);
merge_sort(mid+1,r);
while (i<=mid&&j<=r)
if (a[i]<=a[j]) {
c[k]=a[i]; i++; k++;
} else {
c[k]=a[j]; ans+=mid-i+1; j++; k++;
}
while (i<=mid) {
c[k]=a[i]; i++; k++;
}
while (j<=r) {
c[k]=a[j]; j++; k++;
}
for (int i=l;i<=r;i++)
a[i]=c[i];
}
int main()
{
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
for (i=1;i<=n;i++)
{
p=i;
while (a[i]>a[i+1]&&i<n) i++;
if (p+1<=i) {
ans++;
for (j=p;j<=i;j++) c[p+i-j]=a[j];
for (j=p;j<=i;j++) a[j]=c[j];
}
}
for (i=1;i<=n;i++) c[i]=0;
merge_sort(1,n);
printf("%lld",ans);
}