BZOJ4204: 取球游戏
BZOJ4205: 卡牌配对

BZOJ4240: 有趣的家庭菜园

shinbokuow posted @ Aug 18, 2015 08:32:43 PM in BZOJ with tags 贪心 逆序对 , 1176 阅读

题解:

首先预处理出$l_i$表示第$i$个数左侧比第$i$个数大的个数,$r_i$右侧同理.

首先脑补出我们应该将序列排成先单调不降,后单调不增的形式.

我们从大到小将数插入序列,考虑插入某个数,比这个数大的已经全部插入完毕了,由于排成那种形式,我们只能将这个数放在当前序列的头或尾.

若放在开头,则带来$l_i$的逆序对;否则带来$r_i$的逆序对.

显然每个数之间都是独立的,我们直接贪心就行了.

时间复杂度$O(nlogn)$.

代码:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;

#define N 300010
int n,_n,a[N],b[N];

inline int getc(){
	static const int L=1<<15;
	static char buf[L],*S=buf,*T=buf;
	if(S==T){
		T=(S=buf)+fread(buf,1,L,stdin);
		if(S==T)
			return EOF;
	}
	return*S++;
}
inline int getint(){
	int c;
	while(!isdigit(c=getc()));
	int x=c-'0';
	while(isdigit(c=getc()))
		x=(x<<1)+(x<<3)+c-'0';
	return x;
}

int A[N],l[N],r[N];
inline int ask(int x){
	int re=0;
	for(;x;x-=x&-x)
		re+=A[x];
	return re;
}
inline void upd(int x){
	for(;x<=_n;x+=x&-x)
		++A[x];
}
int main(){
	n=getint();
	int i,j;
	for(i=1;i<=n;++i)
		a[i]=b[i]=getint();
	sort(b+1,b+n+1);
	_n=unique(b+1,b+n+1)-b-1;
	for(i=1;i<=n;++i)
		a[i]=lower_bound(b+1,b+_n+1,a[i])-b;
	for(i=1;i<=n;++i){
		l[i]=i-1-ask(a[i]);
		upd(a[i]);
	}
	memset(A,0,sizeof A);
	for(i=n;i>=1;--i){
		r[i]=n-i-ask(a[i]);
		upd(a[i]);
	}
	long long ans=0;
	for(i=1;i<=n;++i)
		ans+=min(l[i],r[i]);
	cout<<ans<<endl;
	return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter