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

      

  • 相关阅读:
    hihoCoder #1176 : 欧拉路·一 (简单)
    228 Summary Ranges 汇总区间
    227 Basic Calculator II 基本计算器II
    226 Invert Binary Tree 翻转二叉树
    225 Implement Stack using Queues 队列实现栈
    224 Basic Calculator 基本计算器
    223 Rectangle Area 矩形面积
    222 Count Complete Tree Nodes 完全二叉树的节点个数
    221 Maximal Square 最大正方形
    220 Contains Duplicate III 存在重复 III
  • 原文地址:https://www.cnblogs.com/lsy263/p/11227965.html
Copyright © 2011-2022 走看看