线段树求逆序对(hdu1394Minimum Inversion Number)

        说实话,线段树求逆序对我理解了半天诶,不知是否有人像我一样。

        对于每个数来说,只有和已经出现过的、比它大的数才能形成逆序对,那么在给定的数列中,每给一个数就向前找比它大的数。

        样例:10

                    1 3 6 9 0 8 5 7 4 2

                    首先将数组清0,0~9(n-1=9):0 0 0 0 0 0 0 0 0 0

                    出现“1” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 0 0 0 0 0 0 0

                    出现“3” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 0 0 0 0

                    出现“6” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 6 0 0 0

                    出现“9” ,且现在数组中没有比它大的数,逆序对数sum+0,0~9(n-1=9):0 1 0 3 0 0 6 0 0 9

                    出现“0” ,0后有4个数,逆序对数sum+4,0~9(n-1=9):0 1 0 3 0 0 6 0 0 9

                    以此类推。在每次查找完后将这个数加入数列。

                    在把原数列找完后,要开始将第一个数挪到后面去,比这个数 x 小的有 x 个,比这个数大的有 n-1-x个,于是 sum += (n-1-x) - x 。

#include<stdio.h>
#include<algorithm>
using namespace std;
int n;
int tree[5001*4];
void pushup(int node)
{
    tree[node]=tree[node<<1]+tree[node<<1|1];
    return ;
}
void build(int l,int r,int node)
{
    tree[node]=0;//将数组清0
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1);
}
void update(int x,int l,int r,int node)
{
    if(l==r)
    {
        tree[node]++;//是这个数出现了,并不是赋值
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
    {
        update(x,l,mid,node<<1);
    }else
    {
        update(x,mid+1,r,node<<1|1);
    }
    pushup(node);
}
int query(int l,int r,int st,int en,int node)
{
    if(st>=l&&en<=r)return tree[node];
    int mid=(st+en)>>1;
    int ret=0;    
    if(mid>=l)ret+=query(l,r,st,mid,node<<1);
    if(mid<r)ret+=query(l,r,mid+1,en,node<<1|1);
    return ret;
}
int num[50005];
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        int ans=0;
        build(0,n-1,1);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&num[i]);
            ans+=query(num[i],n-1,0,n-1,1);//询问是否有比 num[i] 大的数
            update(num[i],0,n-1,1); //将 num[i] 更新
        }
        int ret=ans;
        for(int i=0;i<n;i++)
        {
            ans+=n-num[i]-num[i]-1;
            ret=min(ret,ans);
        }
        printf("%d\n",ret);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值