(c) hdu1394* (求逆序对数)(线段树)

Posted lsy263

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(c) hdu1394* (求逆序对数)(线段树)相关的知识,希望对你有一定的参考价值。

(c) hdu1394

如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍

 

线段树求逆序对数比较少见啊(归并排序多快啊...但是本文是讲解线段树写法...),何况这题还加了点别的玩意儿...

1. 本来这种题目要离散化的,可是体中保证了数列0~n-1.

2. 每次把首位放到最末,显然不能 每次都求逆序对 ,于是又到了推  导时间。

由
a1 a2 ... an
变为
a2 a3 ... an a1

减少的逆序对数为 a2~an中比a1小的数的个数
增加的逆序对数为 a2~a1中比a1大的数的个数

因为a1~an的值是0~n-1
所以看其排名即可
即比a1小的有a1个数
   比a1大的有n-a1+1个数

实际上代码中的MAXN=n-1

于是只要求出原序列的逆序对个数即可

 

逆序对

  对于给定的数列 a1 ,a2 ,...,an :如果有 i,j 满足 i < j 且 ai > aj ,我们说 (ai ,aj ) 为一对逆序对。

 

逆序对的朴素求法

  从 1 ∼ n 枚举数列中的每一个数 i,从 1 ∼ i 枚举数列中的每一个数 j,找出所有满足 a j > a i 的数对 (i,j)。

利用线段树优化
  这个优化依赖于桶,如果题目不保证数列中的数的数值大小则需要离散化。按照数列的顺序将数插入 1 ∼ N 的桶中(N 表示桶排序后最大桶的序号),对于每一个 ai 统计 ai +1∼N 桶的和,累加即可。
  也就是依次把每个数放入桶中并查询比他大的且比他先放(排在他前面)的有多少个数

 

代码

 

/*线段树+离散化+桶  求逆序对*/
/*每次往桶里加一个数并查询比它大的数个数*/
/*hdu1394*/
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=2e7+3;
typedef long long LL;
int v[N<<2],n;
LL list[N],num[N],ans=0;
//sum -> for the first seq
#define ls (rt<<1)
#define rs (ls|1)
#define mid (l+r>>1)
#define pushup(rt) v[rt]=v[ls]+v[rs]

void build(int rt,int l,int r)
    if(l==r)v[rt]=0;return;
    build(ls,l,mid);build(rs,mid+1,r);
    pushup(rt);

void update(int rt,int l,int r,int x)
    v[rt]++; 
    if(x==r&&x==l)return;
    if(x<=mid)update(ls,l,mid,x);
    else update(rs,mid+1,r,x);
    //pushup(rt);
    return;

int query(int rt,int l,int r,int x,int y)
    if(x<=l&&y>=r)return v[rt];
    int res=0;
    if(x<=mid)res+=query(ls,l,mid,x,y);
    if(y>mid)res+=query(rs,mid+1,r,x,y);
    return res;

int main()
    while(scanf("%d",&n)!=EOF)
        for(int i=1;i<=n;++i)
            scanf("%lld",&list[i]),list[i]++,num[i]=list[i];
        build(1,1,n);
        ans=0;
        sort(num+1,num+1+n);
        int siz=unique(num+1,num+1+n)-num;
        int MAXN=n;
        /*int MAXN=siz-1; //注意取下标
        for(int i=1;i<=n;++i)
            list[i]=lower_bound(num+1,num+siz,list[i])-num;
        //此时list内的数已离散化
        //build(1,1,MAXN);
        */
        for(int i=1;i<=n;++i)
            update(1,1,MAXN,list[i]); //此数字多了一个
            if(list[i]!=MAXN) ans+=query(1,1,MAXN,list[i]+1,MAXN);
            //不是最大查询比他大的
        
        LL sum=ans;
        for(int i=1;i<=n;++i) //每次把最前一个放到末尾
            int x=list[i]-1,y=MAXN-list[i];             //大的贡献   小的贡献
            //x比他小的(-)   y比他大的(+)
            sum+=y-x;ans=min(sum,ans);
        
        printf("%lld\\n",ans);
    
    return 0;

 

End

  

以上是关于(c) hdu1394* (求逆序对数)(线段树)的主要内容,如果未能解决你的问题,请参考以下文章

hdu1394Minimum Inversion Number(线段树,求最小逆序数)

HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)

HDU 1394 Minimum Inversion Number(线段树:单点更新,区间求和或树状数组:求逆序对)

HDU 1394 Minimum Inversion Number 线段树

[HDOJ1394]Minimum Inversion Number(线段树,逆序数)

线段树-最小逆序数hdu1394