逆序对

学了树状数组后,突然想到了这个逆序对,我也就顺便记了下来,以防以后忘掉后不知所措。(。・・)ノ


逆序对:

一个序列a中,如果有i < j&&a[i]>a[j],那么这就是一个逆序对。
那么如何求一个序列中逆序对的个数?o(^▽^)┛


基本方法:

n2 的两重循环,暴力寻找前面比当前位置大的数。
当然,这是最蠢的方法- -|||


归并排序:

我们可以通过归并排序(从小到大),每次二分区间,在合并时,这两个区间都是单调不下降的。如果出现了左区间的一个数大于右区间的一个数,那么左区间剩下的所有数都会大于这个数,也就是左区间剩下的i-mid+1个数都会大于这个数,那么就会产生i-mid+1个逆序对,加进答案即可。
该算法高效,时间为 O(nlog2n)

void mesort(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2;
    mesort(l,mid);
    mesort(mid+1,r);
    int i=l,j=mid+1,t=l;
    while (i<=mid&&j<=r){
        if (a[i]<=a[j]) fre[t++]=a[i++];
        else {
            fre[t++]=a[j++];
            ans+=mid-i+1;//加入逆序对。
        }
    }
    while (i<=mid) fre[t++]=a[i++];
    while (j<=r) fre[t++]=a[j++];
    fo(i,l,r) a[i]=fre[i];
}

树状数组:

利用该数据结构也是个很快速求逆序对的方法,而且可以在线求(就是在序列尾新加入一个数后求逆序对)。
主要思想是:
新加入一个数x后,当前总共的数的个数为i,那么在树状数组上x的位置增加一,并且利用树状数组求出前面小于等于x的数的个数,那么新增的逆序对的个数就是(i-x)个了。
最后求个和即可。(当然注意要离散化!)时间复杂度: O(nlog2n) .

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=1000;
int fre[maxn],n,ans;

int lowbit(int x)
{
    return x&-x;
}
void add(int k,int x)
{
    while (k<=n){
        fre[k]+=x;
        k+=lowbit(k);
    }
}
int get(int k)
{
    int sum=0;
    while (k>0){
        sum+=fre[k];
        k-=lowbit(k);
    }
    return sum;
}
int main()
{
    scanf("%d",&n);
    fo(i,1,n){
        int x;
        scanf("%d",&x);//注意要离散化,此次没有离散化。 
        add(x,1);
        ans+=i-get(x);//新增的逆序对的个数。
    }
    printf("%d",ans);
}

这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值