洛谷 P1908 逆序对(树状数组+离散化)

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a i > a j a_i>a_j ai>aj
i < j i<j i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

第一行,一个数 n,表示序列中有 n个数。

第二行 n 个数,表示给定的序列。序列中每个数字不超过 1 0 9 10^9 109

输出格式

输出序列中逆序对的数目。

输入输出样例

输入

6
5 4 2 6 3 1

输出

11

在这里插入图片描述

分析

求逆序对最典型的方法就是归并排序,但是还有一种方法就是树状数组。(个人感觉树状数组求逆序对相比归并排序排序要更好理解一些,而且树状数组的代码量也要少一些)

一、 离散化
  数据很大,这会造成我们要开的空间很大,要用离散化压缩数组(可以保证小的数依旧小,大的数依旧大,对结果没有影响)
  👉什么是离散化?

二、求逆序对
  对于离散化后的数组dis,我们可以用桶排序的思想,将 a [ d i s [ i ] ] a[dis[i]] a[dis[i]]加上1,然后我们统计a[1] 到a[dis[i]-1]的和ans,就是在这个数前面有多少个数比它小。

AC的C++代码

#include <iostream>
#include <algorithm>
#define ll long long int
using namespace std;
const ll MAXN=1e5+5;

//a维持树状数组,b为输入的原始数组,dis为b离散化之后的 
ll a[MAXN*5],b[MAXN*5],dis[MAXN*5],n;

ll lowbit(ll x) {
  return x & -x;
}
// 单点修改(第x个整数加v)
void update(ll x, ll v) {
  for(ll i = x; i<= n; i += lowbit(i))
      a[i] += v;
}
// 单点查询
ll getsum(ll x) {  // a[1]……a[x]的和
  ll sum = 0;
  for(ll i = x; i> 0; i -= lowbit(i)){	// 注意是>0
      sum += a[i];	
  }
  return sum;
}


int main(){
	cin >> n;
	for(ll i=1; i<=n; i++)
		cin>>b[i];
	
	// 离散化
	for(ll i=1; i <= n; i++){
		dis[i]=b[i];
	}
	std::sort(b + 1, b + 1 + n);
	ll len = std::unique(b + 1, b + n + 1) - b - 1;// len 为离散化后数组的有效长度(去重之后)
	for(ll i=1; i <= n; i++)
		dis[i] = std::lower_bound(b + 1, b + len + 1, dis[i]) - b; 
	
	ll ans=0;
	for(ll i=n; i >= 1; i--){
		update(dis[i], 1);
		ans += getsum(dis[i]-1); //得到之前有多少个比你大的数(逆序对)
	}
	cout<<ans;
	return 0;
 } 
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值