逆序对数线段树数求法与树状数组求法

在数组a中a[i],a[j]是一个逆序对,当且仅当i<j且a[i]>a[j]时成立。

先考虑下暴力求法:

求以a[i]为大数的逆序对的个数,则我们检索0~i-1中大于a[i]的个数记入总数。

时间复杂度:n^2,复杂度很高。

现在我们考虑下,利用线段树的方法。

我们每拿到一个数我们就必须求出前面比这个数大的数的个数 k,于是我们可以用线段树记下区间数的个数,查询出这k就可以了,每次我们求完一个数的逆序对数就把这数放入线段树并经行维护。

当数非常大时,我们可以离散化下就可以了,以下代码是带有离散处理的。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define model l,mid,num<<1
#define moder mid+1,r,num<<1|1
using namespace std;
typedef long long LL;
int MAX;
const int M=1e5+5;
struct node{
    LL v;//记下数值。
    int x;//记下下标。
    node(){};
    node(LL vv,int xx){v=vv,x=xx;};
};
node record[M];
LL arr[M];
int n;
void poshash()
{
    sort(record+1,record+n+1,[](node a,node b){return a.v<b.v;});
    MAX=1;
    for(int i=1;i<=n;++i)
    {
        if(record[i].v!=record[i-1].v&&i!=1)MAX++;
        arr[record[i].x]=MAX;
    }
    return ;
}
int pre[M<<2];
int query(int L,int R,int l,int r,int num)
{
    if(L>R)return 0;
    if(L<=l&&r<=R)
    {
        return pre[num];
    }
    int mid=(l+r)/2;
    if(R<=mid)return query(L,R,model);
    else if(mid<L)return query(L,R,moder);
    else if(L<=mid&&mid<R)return query(L,R,model)+query(L,R,moder);
}
void update(int L,int R,int key,int l,int r,int num)
{
    if(L<=l&&r<=R)
    {
        pre[num]+=key;
        return ;
    }
    int mid=(l+r)/2;
    if(mid<L)update(L,R,key,moder);
    else if(R<=mid)update(L,R,key,model);
    else if(L<=mid&&mid<R)
    {
        update(L,R,key,model);
        update(L,R,key,moder);
    }
    pre[num]=pre[num<<1]+pre[num<<1|1];
}
int main()
{
    while(cin>>n)
    {
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&arr[i]);
            record[i]=node(arr[i],i);
        }
        poshash();
        memset(pre,0,sizeof(pre));
        int sum=0;
        for(int i=1;i<=n;++i)
        {
            sum+=query(arr[i]+1,MAX,1,MAX,1);
            update(arr[i],arr[i],1,1,MAX,1);
        }
        cout<<sum<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/DA_A_MAO/article/details/82156176