zoukankan      html  css  js  c++  java
  • [线段树] (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
    ",ans);
        }
        return 0;
    }

    End

      

  • 相关阅读:
    HDU 6134 Battlestation Operational(莫比乌斯反演)
    HDU 5514 Frogs(容斥原理)
    HDU 5909 Tree Cutting(FWT+树形DP)
    BZOJ 1030 [JSOI2007]文本生成器(AC自动机)
    BZOJ 2938 [Poi2000]病毒(AC自动机)
    HDU 6118 度度熊的交易计划(费用流)
    HDU 6119 小小粉丝度度熊(Two pointers)
    Codeforces 839E Mother of Dragons(极大团)
    Codeforces 839D Winter is here(容斥原理)
    BZOJ 2434 [Noi2011]阿狸的打字机(AC自动机)
  • 原文地址:https://www.cnblogs.com/lsy263/p/11227965.html
Copyright © 2011-2022 走看看