码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2019-07-22 20:02:17      阅读:132      评论:0      收藏:0      [点我收藏+]

标签:逆序对数   统计   +=   max   line   介绍   sim   多少   main   

(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* (求逆序对数)(线段树)

标签:逆序对数   统计   +=   max   line   介绍   sim   多少   main   

原文地址:https://www.cnblogs.com/lsy263/p/11227965.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!