ACM 逆序对(逆序数)总结

 

最近做题遇到几次逆序数了,今天总结一下,以后遇到了再也不怕了。

 

首先说明一下什么是逆序数,下面是百度的定义

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数

如2431中,21,43,41,31是逆序,逆序数是4。

 

①如何求逆序数

方法如下:

考察每一位,判断从这一位往后有多少小于该位的,结果累加,得到最后结果。

例如,2,4,3,1     先判断2,之后有1是小于2的,结果+1,再判断4,之后3,1都小于4,结果+2,判断3时,结果再+1,最后得到结果4.

至于为什么,我也不知道,反正我感觉挺好理解的。

②如何用算法实现

当然,对于上述简单的过程,我们可以用两个for循环直接暴力,但是,这种方法一般都太low了,复杂度太高。

高效的方法有 : 归并排序、线段树、树状数组。

个人建议使用线段树求解,因为线段树在很多地方都有应用。

对于归并排序和树状数组,我就不解释了,因为归并排序一搞就忘记了而且用处不大,想了解的这里给出一篇博客

https://blog.csdn.net/acm_jl/article/details/51147010

 

例题是 HDU 1394,然后做完这个可以再写一下POJ 2299

HDU 1394的解析如下

https://www.cnblogs.com/qlky/p/5693747.html

然后我写的是POJ 2299

下面开始解析,当然,大家一定要有一定线段树的基础

 

算法的思想还是最基础的思想,考察每个点,判断它以后有多少个小于它的,然后累加。

与之前不同的地方在于,求小于某个数的个数时用到了线段树,所以非常快

这里的线段树表示某个范围内的值个数query 1--3,就表示值在1--3的个数

然后对于1--n个数,我们每个数都进行两步操作

查询1--a[i],单点更新a[i],值+1

例如对于 2 4 3 1 序列,我们从右向左判断,首先查询1-1,没有,值为0,然后1点值更新

再查询1--3,值为1,(实质表示的意思是比3小的有1个),然后更新3,

再查询4,更新4

查询2,更新2,得到最后的结果

 

下面是POJ 2299 的代码,要注意的是要离散化一下,因为值比较大

#include <iostream>
#include <cstdio>
#include<algorithm>
#include <cstring>
#include <stack>
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = 1 ; j <= r ; j++ )
#define lef rt<<1
#define rig rt<<1|1
#define mid (l+r)>>1
#define mem(a,val) memset(a,val,sizeof a)
typedef long long ll;
using namespace std;

struct spe
{
    int val,pos;
};
const int maxn = 5e5+5;
spe p[maxn];
int n;
int pos[maxn];
int tree[maxn<<2];
int L,R;
bool cmp( spe a,spe b )
{
    return a.val &l
  • 14
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值