树状数组求逆序数--poj 2299

突然想起树状数组了,因为突然想起之前从来没A掉的那个树状数组求逆序数的题,大概意思就是求一个数组用冒泡排序排序后的交换次数,因为数据量为5万,所以O(n*n)的冒泡模拟肯定是超时的,所以题目的解法可以有两种,一种是归并排序,归并排序的一个应用正是求逆序数,而另一个就是树状数组了。

思想就是通过将数一个一个的插入到树状数组中,每次统计这个数之前比它大的数的sum值,所以这里树状数组里每次add更新的是比这个数大的数的个数而不是数的值。因为数据里面给的范围是99999999,特别大,所以说需要离散化一下。一开始看别的人的本来我也讨厌离散化的,可惜看着数据量觉得不离散化好像过的费劲(现在看看离散化也没太烦)。为啥需要离散化呢?比如给你5个数,

99999999,1,2,3,4.

这些数里,第一个数需要用的树状数组为c[99999999]这么大,就是这个意思,开不了这么大-_-#,那么我们可以用一个结构体node,里面有俩值,一个存输入的值,一个存这个值的下标:

struct node
{
	int data;	//存值
	int pos;//存下标
};
node p[510000];//离散化的数组

那么我们每次输入的时候就输入到p[i].data,并且p[i].pos = i;

	for(int i = 1 ; i <= n ; i++)
		{
			scanf("%d",&p[i].data);
			p[i].pos = i;
		}

然后我们将这个离散化的数组按照    数值   从小到大排序。

bool cmp(node a,node b)
{
return a.data < b.data;
}
sort(p+1,p+n+1,cmp);

再用一个数组a[510000]来存储排序后的离散化数组

for(int i = 1 ; i <= n ; i++)
     a[ p[i].pos ] = i;
开始的时候,大家到这一步可能有点蒙,好好想想,其实就是这个意思:你要求的数组一共不超过500000个,但是如果用树状数组来求,你就需要最大c[9999999]这么大的,所以我们就将99999999存在p[n].data里面,这时候n最大就是500000吧,所以达到减少内存的目的,这时候上面我给的样例就是

p[i].data: 1,2,3,4,99999

p[i].pos:  2,3,4,5,1

a[ p[i].pos ]:  1,2,3,4,5

也就是 a[2] = 1,a[3] = 2,a[4] = 3,a[5] = 4,a[1] = 5;




接下来就是将每个数插入到树状数组中,并且每次往add里增加的数并不是第i个数的数值data,而是+1,也就是说每次sum统计的是比这数小的数,然后我们用这个i-sum(a[i])得到的就是这个数的逆序数啦

我们将答案加上每个数的逆序数即可

for(int i = 1 ; i <= n ; i++)
		{
			add(a[i],1);
			ans += i-sum(a[i]);
		}
完整的程序:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
struct node
{
	int data;	//存值
	int pos;//存下标
</span>
};
long long ans;
node p[510000];<span style="white-space:pre">	//离散化的数组
int c[510000];<span style="white-space:pre">	//树状数组
int a[510000],n;<span style="white-space:pre">	//存离散化数组的a数组
bool cmp(node a,node b)
{
	return a.data < b.data;
}

int lowbit(int x)
{
	return x&-x;
}
void add(int i,int x)
{
	
	while(i <= n)
	{
		c[i] += x;
		i += lowbit(i); 
	}
	
}
int sum(int i)
{
	int ans = 0;
	while(i > 0)
	{
		ans += c[i];	
		i -= lowbit(i);
	}	
	return ans;
}
int main(int argc, char *argv[])
{
	while(scanf("%d",&n)!=EOF)
	{
		if(n == 0)break;
		ans = 0;
		memset(c,0,sizeof(c));
		for(int i = 1 ; i <= n ; i++)
		{
			scanf("%d",&p[i].data);
			p[i].pos = i;
		}
		
		sort(p+1,p+n+1,cmp);
		
		for(int i = 1; i <= n ; i++)
			a[p[i].pos] = i;
		
		for(int i = 1 ; i <= n ; i++)
		{
			add(a[i],1);
			ans += i-sum(a[i]);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

文笔也不好,要是有啥不对的大家一定告诉我T_T (500多MS过的,弱爆了)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值