jzoj 3462. 休息(rest) (Standard IO)

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);
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章