线段树求逆序数(模板)


经过两天的调试终于弄明白了线段树求逆序数,;

说一下思路:

    首先建树是根据序列中最大的数开辟的区间,(如果数太大 离散化)。

    逆序对可以表示成一个数前面有几个比这个数大的数,就表示这个数所形成的逆序对数,(听绕口的,需要模拟一下);

    数列a[i],将数列a[i]添加到线段树中,并将区间中有它的区间的num+1,意思是在当前的区间上有几个值了,记录下来。

   因为是从a[1]->a[n]遍历的,所以每次加进a[i]时,都查询一下在区间1-a[i]上有几个值了,有几个值就表示在a[i]之前的前i个值中有几个比a[i]小的了。

(讲起来有点啰嗦,直接看代码吧)

#include <iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>

using namespace std;

struct node
{
    int l,r;
    int num;
} A[10000];

int a[1000];

void build(int node,int l,int r)
{
    A[node].l=l;
    A[node].r=r;

    A[node].num=0;
    if(l==r)
    {
        return;
    }

    build(2*node,l,(l+r)/2);
    build(2*node+1,(l+r)/2+1,r);
}

void in(int node,int l,int r,int x)
{
    //cout<<"++"<<node<<" "<<l<<" "<<r<<" "<<x<<endl;
    if(l==A[node].l&&A[node].r==r)
    {
        A[node].num+=x;
       // cout<<node<<" "<<A[node].num<<"***\n";
        //return ;
    }

    else
    {
        int mid=(A[node].l+A[node].r)/2;
        if(l<=mid)
            in(2*node,l,r,x);
        if(r>mid)
            in(2*node+1,l,r,x);
        A[node].num=A[node*2].num+A[node*2+1].num;
    }
}

int query(int node,int l,int r)
{
    if(l<=A[node].l&&A[node].r<=r)
    {
        return A[node].num;
    }
    else
    {
        int ans=0;
        int mid=(A[node].l+A[node].r)/2;
        if(l<=mid)
            ans+=query(node*2,l,r);
        if(r>mid)
            ans+=query(node*2+1,l,r);
        return ans;
    }
}
int main()
{
    int n;
    memset(A,0,sizeof(A));
    cin>>n;
    int mx=0;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
        if(a[i]>mx)mx=a[i];
    }
    build(1,1,mx);

    /*cout<<A[1].num<<"--"<<A[1].l<<" "<<A[1].r<<endl;
    cout<<A[2].num<<"--"<<A[2].l<<" "<<A[2].r<<"\t";
    cout<<A[3].num<<"--"<<A[3].l<<" "<<A[3].r<<endl;
    cout<<A[4].num<<"--"<<A[4].l<<" "<<A[4].r<<"\t";
    cout<<A[5].num<<"--"<<A[5].l<<" "<<A[5].r<<"\t";
    cout<<A[6].num<<"--"<<A[6].l<<" "<<A[6].r<<"\t";
    cout<<A[7].num<<"--"<<A[7].l<<" "<<A[7].r<<endl;
    cout<<A[8].num<<"--"<<A[8].l<<" "<<A[8].r<<"\t";
    cout<<A[9].num<<"--"<<A[9].l<<" "<<A[9].r<<endl;*/

    int sum=0;
    for(int i=1; i<=n; i++)
    {
        int k=a[i];
        in(1,k,k,1);
        /*cout<<"------------------------------------\n";
        cout<<A[1].num<<"--"<<A[1].l<<" "<<A[1].r<<endl;
        cout<<A[2].num<<"--"<<A[2].l<<" "<<A[2].r<<"\t";
        cout<<A[3].num<<"--"<<A[3].l<<" "<<A[3].r<<endl;
        cout<<A[4].num<<"--"<<A[4].l<<" "<<A[4].r<<"\t";
        cout<<A[5].num<<"--"<<A[5].l<<" "<<A[5].r<<"\t";
        cout<<A[6].num<<"--"<<A[6].l<<" "<<A[6].r<<"\t";
        cout<<A[7].num<<"--"<<A[7].l<<" "<<A[7].r<<endl;
        cout<<A[8].num<<"--"<<A[8].l<<" "<<A[8].r<<"\t";
        cout<<A[9].num<<"--"<<A[9].l<<" "<<A[9].r<<endl;
        cout<<"k="<<k<<endl;
        cout<<"kdsjfha=="<<query(1,1,k)<<endl;*/
        sum+=(i-query(1,1,k));

    }
    cout<<sum<<endl;
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值