poj2299 树状数组+离散化

题目链接:http://poj.org/problem?id=2299

大佬1的博客大佬2的博客

题意:大概就是冒泡排序中一共交换了多少次;

离散化:c[]数组用二进制开不了999999999的空间,所以把a[]数组中对应的值给变小作用效果不变

              用结构体 pos表示离散后的值得下标,val表示原来值;

struct node{
	int pos,val;
	bool operator < (const node&b)const{
		return val<b.val;
	}
}p[maxn];

例:9 1 0 5 4 总共要变化6次,离散后变成 5 2 1 4 3也是6次;数值变小作用相同,

在具体用的时候要先对p[]中按val由小到大排序,保证p[].val值和(1,2,3....n)(即下标)大小变化对应。再把离散后的值给a[]

		for(int i=1;i<=n;i++){
			scanf("%d",&p[i].val);
			p[i].pos = i;
		}
		sort(p+1,p+n+1);
		ll ans=0;
		for(int i=1;i<=n;i++)
		a[p[i].pos]=i;

即:a[3]=1,a[2]=2,a[5]=3,a[4]=4,a[1]=5;

之后进行树状数组找逆序数:

每插入一个数, 统计比他小的数的个数,
对应的逆序为 i- sum( a[i] ),
其中 i 为当前已经插入的数的个数,
sum( a[i] )为比 a[i] 小的数的个数,
i- sum( a[i] ) 即比 a[i] 大的个数, 即逆序的个数

因为getsum()是自顶向下的,下面的肯定是比他小,而我们找的是在输入的个数中比他大的个数(这样的话就会形成逆序),就是i- sum( a[i] ) ;

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 510000;
struct node{
	int pos,val;
	bool operator < (const node&b)const{
		return val<b.val;
	}
}p[maxn];
int a[maxn],c[maxn];
int n;
int lowbit(int x){
	return x&(-x);
} 
int getsum(int x){
	int s=0;
	for(int i=x;i;i-=lowbit(i))
	s+=c[i];
	return s;
}
void updata(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i))
	c[i]+=k;
}
int main(){
	while(~scanf("%d",&n)&&n){
		memset(c,0,sizeof c);
		for(int i=1;i<=n;i++){
			scanf("%d",&p[i].val);
			p[i].pos = i;
		}
		sort(p+1,p+n+1);
		ll ans=0;
		for(int i=1;i<=n;i++)
		a[p[i].pos]=i;
		
		for(int i=1;i<=n;i++){
			updata(a[i],1);
			ans+=(i-getsum(a[i]));
		}
		printf("%lld\n",ans);
	}
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值