hdu 1394 Minimum Inversion Number(逆序对数+线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=1394


题意:给定无序的n个数 0~n-1,可以这样变幻得到n的不同的序列:每次将序列中第一个数放到最后一个。

问在这n个序列中逆序对数最少是多少?


思路:

已知逆序对数的定义后,可以直接暴力求原序列的逆序对数,设为sum;可以发现以后n-1个的序列的逆序对数就知道了,不难证明,两个相邻序列的逆序对数差值为 n-1-2*x。

线段树也是直接模拟这个过程。每扫描到a[i],先去查询[a[i]+1,n-1]区间的数的个数,就是a[i]的逆序对数,然后更新a[i]所在区间,将这些逆序对数累加。

首先建树,节点中有一个num域,如果某个数在该区间中,那么该区间的num值加1。相当于数x在线段树中走一遍,用num来记录它的足迹(个人理解)。每输入一个数x就查询在区间[x+1,n-1]中的num值,也就是在输入x之前比x大的数的个数,正好对应逆序对数的定义。然后再把x插入线段树中。这样就把原序列的逆序对数求出来了。


#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
//#define LL long long
#define LL __int64
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 5010;

struct node
{
	int l,r;
	int num;
}tree[maxn*4];

int a[maxn];

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].num = 0;
	if(l == r)
		return;
	int mid = (l+r) >> 1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
}

int query(int v, int l, int r)
{
	if(tree[v].l == l && tree[v].r == r)
		return tree[v].num;
	int mid = (tree[v].l + tree[v].r) >> 1;
	if(r <= mid)
		return query(v*2,l,r);
	else if(l > mid)
		return query(v*2+1,l,r);
	else return query(v*2,l,mid) + query(v*2+1,mid+1,r);
}

void update(int v, int x)
{
	if(tree[v].l <= x && tree[v].r >= x)
		tree[v].num++;
	if(tree[v].l == tree[v].r)
		return;
	int mid = (tree[v].l + tree[v].r) >> 1;
	if(x <= mid)
		update(v*2,x);
	else
		update(v*2+1,x);
}

int main()
{
	int n;
	int ans;
	while(~scanf("%d",&n))
	{
		build(1,0,n-1);
		ans = 0;
		for(int i = 0; i < n; i++)
		{
			scanf("%d",&a[i]);
			if(i != 0 && a[i] != n-1)
				ans += query(1,a[i]+1,n-1);
			update(1,a[i]);
		}
		int res = ans;
		for(int i = 0; i < n; i++)
		{
			ans += n-1-2*a[i];
			if(res > ans)
				res = ans;
		}
		printf("%d\n",res);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值