逆序数 (树状数组+数组离散化)


title: 逆序数 (树状数组+数组离散化)
tags: [树状数组]

描述

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

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

输入

第一行输入一个整数T表示测试数据的组数(1<=T<=5)每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。

输出

输出该数列的逆序数输出该数列的逆序数

分析

这题还是用树状数组来求逆序数,但是输入的数据很多,数组是不能开这么大的。这时就要把数组离散。

举个例子,有四个数99999999 1 123 1583 数据范围太大,而树状数组中的c数组开的范围是数据的范围,这时候就需要离散化,把四个数一次标号为1 2 3 4(即第一个数,第二个数。。。),按键值排序之后 依次为2 3 4 1(即从小到大排序为第二个数,第三个数。。。),所以,第二个数是最小的,即f[2]=1,f[3]=2,f[4]=3,f[1]=4,也就是把键值变为了1~n,相对大小还是不变的,即4  1 2 3。比如要求原来四个数的逆序数总和,现在就是求4 1 2 3的逆序数总和,大大节省了空间压力(树状数组的长度是数据范围)

代码

#include<bits/stdc++.h>
using namespace std;
int e[1000000+50];

///数组的离散化
struct node
{
    int id;
    int val;

} a[1000000+50];

bool cmp(node a,node b)
{
    if(a.val!=b.val)//注意排序方式
        return a.val<b.val;
    return a.id<b.id;
}
int lowBit(int x)
{
    return x&(-x);
}
int SUM(int n)
{
    int sum=0;
    while(n>0)
    {
        sum+=e[n];
        n-=lowBit(n);//从父节点向下加
    }
    return sum;
}
void updata(int id,int v,int n)
{
    while(id<=n)
    {
        e[id]+=v;
        id+=lowBit(id);//从底部向父节点更新
    }
}
int main()
{
    //freopen("2.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        long long  int sum=0;
        memset(a,0,sizeof(a));
        memset(e,0,sizeof(e));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i].val);
            a[i].id=i;
        }
        sort(a+1,a+n+1,cmp);
        for(int i=1; i<=n; i++)
        {
            updata(a[i].id+1,1,n+1);
            sum+=i-SUM(a[i].id+1);
        }
        printf("%lld
",sum);
    }
    return 0;
}
 
原文地址:https://www.cnblogs.com/dccmmtop/p/6710386.html