POJ - 2299 Ultra-QuickSort 解题报告(树状数组)(求逆序数)(离散化)

目录

  • 题目描述
  • 思路分析
  • AC代码

题目描述

链接:https://vjudge.net/problem/POJ-2299
题意:给你一组数,你能做的操作是交换两个相邻的数,令这个数列变成递增的,要求最小操作数。

思路分析

说实话,看到这样的题,感觉我已经本能反应地想到了逆序数了。。不知道是不是最近看了点线代的缘故。。
具体分析起来是这样的:
5
9 1 0 5 4
以样例为例,我们一个一个的输入,那么输入到1的时候,9和1是逆序对,需要交换一次。输入,变成1 9,输入0,0和9是逆序对,需要交换,变成1 0 9,0和1又是逆序对,变成0 1 9。感受到没有?
1往前的和1组成的逆序对有9 1这一对,0往前的和0组成的逆序对有9 0,1 0这两对……这不就变成了求逆序数了吗?
确定了它原本的问题之后,我们就要想方法解决。之前接触过归并排序计算的方法,这次就尝试了用树状数组。首先,怕数与数之间差距太大造成浪费,需要离散化。离散化后为1~n之间的数。之后,需要想怎么计算逆序数。
我们知道,树状数组的一个基本操作就是求前缀和,我们可以利用这个来实现:每次插入一个数ai之后,我们可以计算一下目前树里边处于1~ai的值一共有多少个。假设说这个序列刚好是递增的,那么,1 ~ai的个数应该刚好是ai。首先,这个个数是不可能超过ai的(对于3顶多也就1 2放在它前面,不可能再出现一个正整数比3小的),如果这个数小于ai,那就说明有的数现在还没有被加到树上(比如:1 3,前缀和sum(3)=2,少了一个2),这个数则会再后面出现,和现在的树组成逆序数。
例如:
还是拿样例说话
9 1 0 5 4
离散化后为5 2 1 4 3
树插入5之后,树中1~5的数有一个,5-1=4,还有4个本来在5前面的数没有出现,所以ans+=4
插入2后,树中1~2范围的数有1个,ans+=2-1=1
插入1后,1~1范围内的数有1个,ans+=1-1=0
……
以此类推,可以求出逆序数的个数。

AC代码

#include 
#include 
#include 
#include 
#include 
#define MEM(a) memset(a,0,sizeof(a))
#define lowbit(x) ((x)&-(x))
using namespace std;
typedef long long ll;
const int maxn= 500000+10;
ll n;
ll tree[maxn]={0};
ll nums[maxn]={0};
ll lnums[maxn]={0};
ll gg[maxn]={0};

ll sum(ll x){
	ll sum=0;
	while(x>0){
		sum+=tree[x];
		x-=lowbit(x);
	}
	return sum;
}

void add(ll x,ll d){
	while(x<=n){
		tree[x]+=d;
		x+=lowbit(x);
	}
}

int main()
{
	while(scanf("%d",&n)&&n){
		MEM(tree);
		MEM(gg);
		MEM(lnums);
		MEM(nums);
		for(int i=0;i<n;i++) 
		{
			scanf("%d",nums+i);
			lnums[i]=nums[i];
		}
		sort(lnums,lnums+n);
		for(int i=0;i<n;i++){
			gg[i]=lower_bound(lnums,lnums+n,nums[i])-lnums+1;
		}
		//cout<<"ok"<
		ll ans=0;
		for(int i=0;i<n;i++){
			add(gg[i],1);
			ans+=gg[i]-sum(gg[i]);
		}
		cout<<ans<<endl;
	}
	return 0;
}

你可能感兴趣的:(数据结构)