HDU 1394 线段树求逆序数

1.首先要用到一个结论:

对于 0 到 n-1 的n个数任意排列,当前总逆序数为sum,那么把第一个数x放到末尾之后,总逆序数变为 sum-x+n-x-1;

可以这么想:x本来是在最前面的,那么在x之后比x小的个数就是 x 。(因为是0,1,2....n-1排列)

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

在3这个数移到末尾之后,就损失了这3个逆序数对。所以,sum-=3;

但是必定又多了几个,因为一共n个数,刚才比x小的是x个数,那么还剩下 n - x个数不小于 x,除去本身,即有n-x-1个数比x大,现在x到最后了,那么这些数都会造成逆序数的增加,所以sum+=n-x-1。

2.知道了这个之后,我们就可以直接求出初始序列的逆序数,再推导最小的就是了

3.每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结果了逆序数的统计。

线段树可以将这个查询操作控制在logn之内,所以不会超时

实现方法:先建一座空树,seg[i].value=0;

加入一个点,就把这个点变成1,并更新被它影响的父亲节点。

然后查询在它前面比它大的数的个数ret,sum+=ret;

试想:在插入x之前,已经插入了比x大的数(此时它的value应从0变成1了),那么当我们查询(x,n-1]的时候,ret+=(x,n-1].value,就把比x大的个数加进去了。


代码:

<span style="font-size:10px;">#include <iostream>
#include <stdio.h>
#include <algorithm>
#define maxn 50005
using namespace std;
int n,a[maxn];
struct node
{
    int left,right;
    int value;
}seg[maxn*4];

void build(int i,int l,int r)
{
    seg[i].left=l;
    seg[i].right=r;
    seg[i].value=0;
    if(l==r)   return ;
    build(i<<1,l,(l+r)>>1);
    build(i<<1|1,((l+r)>>1)+1,r);
}

void updata(int i,int x){
    if(seg[i].left==seg[i].right )
    {
            seg[i].value++;
            return ;
    }
    int mid=(seg[i].left+seg[i].right)>>1;
    if(x<=mid)
        updata(i<<1,x);
    else
        updata(i<<1|1,x);
    seg[i].value=seg[i<<1].value+seg[i<<1|1].value;
}

int query(int i,int l,int r)
{
    if(seg[i].left==l && seg[i].right==r)
    {
        return seg[i].value;
    }
    i<<=1;
    int ret=0;
    if(l<=seg[i].right)
        ret+=query(i,l,min(seg[i].right,r));
    i++;
    if(r>=seg[i].left)
        ret+=query(i,max(l,seg[i].left),r);
    return ret;
}
/*
int query(int i,int l,int r) {
    if (seg[i].left>=l && seg[i].right<=r) {
        return seg[i].value;
    }
    int mid=(seg[i].left+seg[i].right)>>1;
    int ret = 0;
    if (l <= mid) ret += query(i<<1,l,r);
    if (r > mid) ret += query(i<<1|1,l,r);
    return ret;
}
*/
int main()
{
    while(~scanf("%d",&n))
    {
        build(1,0,n-1);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            updata(1,a[i]);
            if(a[i]!=n-1)
                ans+=query(1,a[i]+1,n-1);
        }
        int mi=ans;
        for(int i=1;i<=n;i++)
        {
            ans=ans+n-a[i]-a[i]-1;
            mi=min(ans,mi);
        }
        printf("%d\n",mi);

    }
    return 0;
}
</span>



你可能感兴趣的:(HDU 1394 线段树求逆序数)