HDU 1394(线段树)

转自:http://wenku.baidu.com/view/6e02b7492e3f5727a5e9623f.html

如何求最小逆序数呢?(我这里假设一个序列中每个数字都不同) 

若abcde...的逆序数为k,那么bcde...a的逆序数是多少?我们假设abcde...中小于a的个数为t-1 , 那么大于a的个数就是n - t,当把a移动左移一位时,原来比a大的现在都成了a的逆序对,即逆序数增加n -t,但是原来比a小的构成逆序对的数,现在都变成了顺序,因此逆序对减少 t - 1,所以新序列的逆序数为 k += n - t - t + 1,即k += n + 1 - 2 * t , 于是我们只要不断移位(n次) 然后更新最小值就可以了.

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;

const int maxn=5010;

int a[maxn],n;
int main(){
	while (scanf("%d",&n)==1)
	{
		for (int i=0;i<n;++i)scanf("%d",&a[i]);
		int sum=0,ans=INT_MAX;
		for (int i=0;i<n;++i)
		{
			for (int j=0;j<i;++j)
			{
				if(a[j]>a[i])sum++;
			}
		}
		for (int i=0;i<n;++i)
		{
			sum=sum+(n-a[i])-(a[i]+1);
			if(sum<ans)ans=sum; 
		}
		printf("%d\n",ans);
	}
	return 0;
}


线段树解法:读入a[i]的时候,在线段树中查询a[i]-n的值是否存在,存在的话说明在a[i]之前插入了a[i]-n的值,构成了逆序对,然后跟上面的公式一样递推出来

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;

const int maxn=5010;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

int segs[maxn<<2];
void pushUp(int rt){
	segs[rt]=segs[rt<<1]+segs[rt<<1|1];
}
void build(int l,int r,int rt){
	segs[rt]=0;
	if(l==r)return;
	int m=(l+r)>>1;
	build(lson),build(rson);
	pushUp(rt);
}

int query(int L,int R,int l,int r,int rt){
	if(L<=l&&r<=R)return segs[rt];
	int m=(l+r)>>1,ret=0;
	if(L<=m)ret+=query(L,R,lson);
	if(R>m)ret+=query(L,R,rson);
	return ret;
}

void update(int p,int l,int r,int rt){
	if(l==r){
		segs[rt]++;
		return;
	}
	int m=(l+r)>>1;
	if(p<=m)update(p,lson);
	else update(p,rson);
	pushUp(rt);
}
int a[maxn],n;
int main(){
	while (scanf("%d",&n)==1)
	{
		build(0,n-1,1);
		int sum=0,ans=0;
		for (int i=0;i<n;++i)
		{
			scanf("%d",&a[i]);
			sum+=query(a[i],n-1,0,n-1,1);
			update(a[i],0,n-1,1);
		}
		ans=sum;
		for (int i=0;i<n;++i)
		{
			sum=sum+(n-a[i])-(a[i]+1);
			if(sum<ans)ans=sum; 
		}
		printf("%d\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值