• poj 2104 K-th Number 划分树


    划分树是在建树的过程中保存快速排序。

    划分树
    同样以1 5 2 6 3 7为例:
    根据中位数mid,将区间划分成左子树中的数小于等于mid,右子树中的数大于等于mid,得到这样一棵划分树:
            [1 5 2 6 3 7]
         [1 2 3]      [5 6 7]
       [1 2]  [3]    [5 6] [7]
      [1] [2]        [5] [6] 
    注意要保持下标的先后顺序不变
    对每一个区间,用sum[i]记录区间的左端点left到i有几个进入了左子树,即有几个数小于等于mid
    用对应的下标区间建线段树:(这里下标区间对应的是排序后的数列)
                [1 6]
         [1 3]      [4 6]
      [1 2] [3]   [4 5][6]
      [1][2]      [4][5]
    每次查找[l r]区间的第k大数时,先查看当前区间[left right]下的sum[r] - sum[l - 1]是否小于等于k,如果是,则递归到左子树,并继续在[left + sum[l - 1], left + sum[r] - 1]中找第k大数;
    否则,进入右子树,继续在[mid + l - left + 1 - sum[l - 1], mid + r - left + 1 - sum[r]]找第k - sum[r] + sum[l - 1]大数
    这样一次查询只要logn的复杂度

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define ls (rt<<1)
    #define rs ((rt<<1)|1)
    #define mid ((t[rt].l+t[rt].r)>>1)
    const int maxn = 100010;
    struct node {
        int l , r;
    }t[maxn<<2];
    int sa[maxn],num[20][maxn],cnt[20][maxn];  //sa中是排序后的,num记录每一层的排序结果,cnt[deep][i]表示第deep层,前i个数中有多少个进入左子树  
    int n , q;
    void build(int l,int r,int rt,int deep) {
        t[rt].l = l; t[rt].r = r;
        if(l == r) return;
        int mid_val = sa[mid],lsum=mid-l+1;
        for(int i=l;i<=r;i++)
            if(num[deep][i] < mid_val)
                lsum --; //lsum表示左子树中还需要多少个中值  
        int L = l , R = mid + 1;
        for(int i=l;i<=r;i++) {
            if(i == l) cnt[deep][i] = 0;
            else cnt[deep][i] = cnt[deep][i-1];
            if(num[deep][i]<mid_val || num[deep][i]==mid_val && lsum>0) {
                num[deep+1][L++] = num[deep][i];
                cnt[deep][i] ++;
                if(num[deep][i] == mid_val)
                    lsum --;
            }
            else num[deep+1][R++] = num[deep][i];
        }
        build(l,mid,ls,deep+1);
        build(mid+1,r,rs,deep+1);
    }
    int query(int l,int r,int rt,int k,int deep) {
        if(l == r) return num[deep][l];
        int s1 , s2; //s1为[tree[step].left,l-1]中分到左子树的个数  
        if(t[rt].l == l) s1 = 0;
        else s1 = cnt[deep][l-1];
        s2 = cnt[deep][r] - s1; //s2为[l,r]中分到左子树的个数  
        if(k <= s2) //左子树的数量大于k,递归左子树 
            return query(t[rt].l+s1,t[rt].l+s1+s2-1,ls,k,deep+1);
        int b1 = l-1-t[rt].l+1-s1; //b1为[tree[step].left,l-1]中分到右子树的个数  
        int b2 = r-l+1-s2; //b2为[l,r]中分到右子树的个数 
        return query(mid+1+b1,mid+1+b1+b2-1,rs,k-s2,deep+1);
    }
    int main() {
        while(~scanf("%d%d",&n,&q)) {
            for(int i=1;i<=n;i++) {
                scanf("%d",&num[1][i]);
                sa[i] = num[1][i];
            }
            sort(sa+1 , sa+n+1);
            build(1,n,1,1);
            int l , r , k;
            while(q--) {
                scanf("%d%d%d",&l,&r,&k);
                printf("%d
    " , query(l,r,1,k,1));
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    Python 基础 字符串拼接 + if while for循环
    JTable 的使用方法
    java与数据库连接的几个步骤
    socket通信 _ 一个简单的群聊系统
    基本的文件读写
    多线程之碰撞小球
    java类的继承
    java类和对象
    java中的关键字
    java 线程实现方式
  • 原文地址:https://www.cnblogs.com/tobec/p/3235837.html
Copyright © 2020-2023  润新知