1. 程式人生 > >【BZOJ4240】有趣的家庭菜園 樹狀數組+貪心

【BZOJ4240】有趣的家庭菜園 樹狀數組+貪心

main etc ron 可能性 tchar -s return 可能 include

【BZOJ4240】有趣的家庭菜園

Description

對家庭菜園有興趣的JOI君每年在自家的田地中種植一種叫做IOI草的植物。JOI君的田地沿東西方向被劃分為N個區域,由西到東標號為1~N。IOI草一共有N株,每個區域種植著一株。在第i個區域種植的IOI草,在春天的時候高度會生長至hi,此後便不再生長。 為了觀察春天的樣子而出行的JOI君註意到了IOI草的配置與預定的不太一樣。IOI草是一種非常依靠陽光的植物,如果某個區域的IOI草的東側和西側都有比它高的IOI草存在,那麽這株IOI草就會在夏天之前枯萎。換句話說,為了不讓任何一株IOI草枯萎,需要滿足以下條件: 對於任意2<=i<=N-1,以下兩個條件至少滿足一個: 1. 對於任意1<=j<=i-1,hj<=hi
2. 對於任意i+1<=j<=N,hk<=hi IOI草是非常昂貴的,為了不讓IOI草枯萎,JOI君需要調換IOI草的順序。IOI草非常非常的高大且纖細的植物,因此JOI君每次只能交換相鄰兩株IOI草。也就是說,JOI君每次需要選擇一個整數i(1<=i<=N-1),然後交換第i株IOI草和第i+1株IOI草。隨著夏天臨近,IOI草枯萎的可能性越來越大,因此JOI君想知道讓所有IOI草都不會枯萎的最少操作次數。 現在給出田地的區域數,以及每株IOI草的高度,請你求出讓所有IOI草的不會枯萎的最少操作次數。

Input

第一行一個正整數N,代表田地被分為了N個區域。 接下來N行,第i行(1<=i<=N)一個整數hi,表示第i株植物在春天時的高度

Output

輸出一行一個整數,表示最少需要的操作次數

Sample Input

6
2
8
4
5
3
6

Sample Output

3

HINT

最終的高度序列為2 4 5 8 6 3,共需要操作三次。 3<=N<=3*10^5 1<=hi<=10^9

題解:一個經常用到的結論:我們給原數組標上下表1...n,移動若幹次後我們會得到一個新的下標序列,需要的搬運次數就是這個新序列的逆序對數。

所以我們思考怎樣讓新序列的逆序對數更少就行了。題中要求最終序列滿足h值先遞增再遞減,所以最大的一定放到中間,次大的可以放在最大的兩邊,再次的放在前兩大的兩邊。。。所以,當放入第k大的時候,我們貪心選擇可以形成較少的逆序對的那邊放,並且這樣做不會影響到後面的節點。

還有如果多個草高度相同要特判一下。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=300010;
typedef long long ll;
ll ans;
int n,l,r;
int s[maxn],v[maxn],p[maxn];
void updata(int x,int val)
{
	for(int i=x;i<=n;i+=i&-i)	s[i]+=val;
}
int query(int x)
{
	int i,ret=0;
	for(i=x;i;i-=i&-i)	ret+=s[i];
	return ret;
}
bool cmp(int a,int b)
{
	return v[a]>v[b];
}
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	scanf("%d",&n);
	int i,j,a;
	for(i=1;i<=n;i++)	v[i]=rd(),p[i]=i;
	sort(p+1,p+n+1,cmp);
	for(i=1;i<=n;)
	{
		for(j=i;j<=n;j++)
		{
			a=query(p[j]),ans+=min(a,i-1-a);
			if(v[p[j+1]]!=v[p[j]])	break;
		}
		for(;i<=j;i++)	updata(p[i],1);
	}
	printf("%lld",ans);
	return 0;
}

【BZOJ4240】有趣的家庭菜園 樹狀數組+貪心