逆序数的几种求法

首先,逆序数的定义

什么叫逆序数

对于某一个数来说,它的逆序数等于在它之前有多少个比它大的数

对于某一个序列来说,逆序数等于所有数的逆序数之和

例如

 序列      5   1    5   2

逆序数   0   1    0   2

序列的逆序数 1+2=3

 

来看逆序数的求法

方法一

首先将定义一个结构体,存数列的值和下标,然后按数值从大到小(数值相同按下标从大到小)sort一下

 

然后建立树状数组,从最大的元素开始,将其标记,即   add(p【i】.id,1)

利用其query(i)查询当前1----i的和,即从1-----i一共有多少个标记的数

对于第i大的数,由于之前所有比它大的数已经标记,所以query(i)就是当前数的逆序数

 

对于序列的逆序数,只需要加起来就可以了

 

代码

 

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define minn(x,y,z) min(min(x,y),z)
struct node
{
    int x,i;
    bool operator < (const node &a) const
    {
        if(x!=a.x)
            return x<a.x;
        return i<a.i;
    }
}p[100005];
int c[100005];
int lowbit(int k)
{
    return k&(-k);
}

void add(int k)
{
    while(k < 100005)
    {
        c[k] += 1;
        k += lowbit(k);
    }
}

int query(int k)
{
    int sum = 0;
    while(k)
    {
        sum += c[k];
        k -= lowbit(k);
    }
    return sum;
}

int main(){
    int n;
    while(~scanf("%d",&n))
    {
        memset(c,0,sizeof(c));
        for(int i = 1; i <= n; i++)
            scanf("%d",&p[i].x),p[i].i=i;

        sort(p+1,p+1+n);

        ll cnt = 0;

        for(int i=n;i>0;i--)
        {
            int ccc =p[i].i;
            cnt = cnt + query(ccc);
            add(ccc);
        }
        printf("%lld\n",cnt);
    }
    return 0;
}

方法二

 

也是树状数组,不过要离散化处理

所谓的离散化就是将数组排个序,例如从小到大排序,

那么将第一小记做1,第二小记做2,那么就算是离散化了

(离散,字面意思,分离,散开,分成一个个的小块,当然每个块不能相同)

这个处理的话就是这样

         序列      5   1    5   2

离散化数组   3   1    3   2

这样从最小的数开始建立树状数组(标记)

那么i-query(b【i】)就是当前数的逆序数(i为当前第i个的数,b【i】是当前数是第几小,

query询问的是1------i有多少个标记的数(小于等于当前数的数),i-query(b【i】)自然是大于当前数  的数  的个数)

补充一个

离散化三部曲:

1. 数组 ha[] 存储所有存在过的数据,sort排序

2. 对ha数组进行去重,重复的数据只保留一个。unique去重(unique函数前提有序)

3. 查询某个数字离散化之后对应的数字,lower_bound查排名

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,tree[100010];
void add(int k,int num)
{
    while(k<=n)
    {
        tree[k]+=num;
        k+=k&-k;
    }
}

int query(int k)
{
    int sum=0;
    while(k)
    {
        sum+=tree[k];
        k-=k&-k;
    }
    return sum;
}
struct node
{
    int x,i;
    bool operator < (const node &a) const
    {
        return x<a.x;
    }
}p[100010];
int b[100010];
int main(void)
{
    int i,j,x,y;

    while(~scanf("%d",&n))
    {
        ll sum = 0;
        memset(tree,0,sizeof(tree));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&p[i].x);
            p[i].i = i;
        }
        sort(p+1,p+1+n);
        int cnt = 1;
        for(i=1;i<=n;i++)///离散化
        {
            if(i != 1 && p[i].x != p[i-1].x)
                cnt++;
            b[p[i].i] = cnt;
        }
        for(i=1;i<=n;i++)
        {
            add(b[i],1);
            sum += (i-query(b[i]));
        }
        printf("%lld\n",sum);
    }
    return 0;
}

 

方法三

 

归并求解

相当漂亮的写法,让我写肯定写不了这么好,但是有个问题是b数组的申请,这里频繁申请爆掉了

我给改成了全局变量

#include <bits/stdc++.h>

using namespace std;

long long int cnt;
int a[104000];
int *b;
void Merge(int a[], int low, int mid, int high);
void Merge_sort(int a[], int low, int high);///归并排序

int main(){
    int n, i;
    scanf("%d", &n);
    for(i = 0; i < n; i++)
        scanf("%d", &a[i]);
    b = new int[n];
    cnt = 0;
    Merge_sort(a, 0, n-1);
    delete[] b;
    printf("%lld\n", cnt);
    return 0;
}
void Merge_sort(int a[], int low, int high){
    int mid;
    if(low < high){
        mid = (low + high)/2;
        Merge_sort(a, low, mid);
        Merge_sort(a, mid+1, high);
        Merge(a, low, mid, high);
    }
}
void Merge(int a[], int low, int mid, int high){///可类比两组有序链表的归并,思想基本一样
    int i = low;
    int j = mid + 1;
    int k = 0;
    //int *b = new int[high-low+1];///动态申请内存
    while(i <= mid && j <= high){
        if(a[i] <= a[j])
            b[k++] = a[i++];
        else {
            b[k++] = a[j++];
            cnt += (mid - i + 1);///归并两组有序数据,当a[i] > a[j], 则在区间[i, mid]的数据全部大于a[j],此时对于a[j]的逆序数为(mid - i + 1)
        }
    }
    while(i <= mid){
        b[k++] = a[i++];
    }
    while(j <= high){
        b[k++] = a[j++];
    }
    for(k = 0, i = low; i <= high; i++, k++){
        a[i] = b[k];
    }
}

方法四

直接数 的就不写了

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值