zoukankan      html  css  js  c++  java
  • poj 2104 主席树

       附上题目链接: http://poj.org/problem?id=2104   很经典的一道主席树题, 题意是查询区间的第k大, http://blog.csdn.net/famousdt/article/details/7064866 这个博主讲的相当赞, 这里附上他的原话:

    建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入它的左子树[l,mid-1],大于它的划入右子树[mid,r]。
    同时,对于第i个数a[i],记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
    建树的时候要注意,对于被分到同一子树的元素,元素间的相对位置不能改变。

    查找的过程中主要问题就是确定将要查找的区间。
    查找深度为dep,在大区间[L ,R]中找小区间[l ,r]中的第k元素。
    我们的想法是,先判断[l ,r]中第k元素在[L ,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的l==r为止。 
    通过之前的记录可以知道,在区间[L,l-1]中有(toleft[dep][l-1]-toleft[dep][L-1])进入左子树,
    记它为x。

    同理区间[L,r]中有(toleft[dep][r]-toleft[dep][L-1])个数进去左子树,记它为y。
    所以,我们知道区间小区间[l,r]中有(y-x)个数进入左子树。那么如果(y-x)>=k,那么就在左子树中继续查找,否则就在右子树中继续查找。

      接着,解决查找的小区间的问题。 
      如果接下来要查找的是左子树,那么小区间应该是[L+([L,l-1]区间进入左子树的个数),L+([L,r]区间内进入左子树的个数)-1]。即区间[L+x,L+y-1]。
        显然,这里k不用变。 
      如果接下来要查找的是右子树,那么小区间应该是[mid+([L,l-1]区间中进入右子树的个数),mid+([L,r]区间进入右子树的个数)-1]。
        即区间[mid+(l-L-x),mid+(r-L-y)]。
        显然,这里k要减去区间里已经进入左子树的个数,即k变为k-(y-x)。

    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    #define MAX_SIZE 100005
    int sorted[MAX_SIZE];//已经排好序的数据
    int toleft[25][MAX_SIZE];
    int tree[25][MAX_SIZE];
    void build_tree(int left, int right, int deep)
    {
        int i;
        if (left == right) return ;
        int mid = (left + right) >> 1;
        int same = mid - left + 1; //位于左子树的数据
        for (i = left; i <= right; ++i) {//计算放于左子树中与中位数相等的数字个数
            if (tree[deep][i] < sorted[mid]) {
                --same;
            }
        }
        int ls = left;
        int rs = mid + 1;
        for (i = left; i <= right; ++i) {
            int flag = 0;
            if ((tree[deep][i] < sorted[mid]) || (tree[deep][i] == sorted[mid] && same > 0)) {
                flag = 1;
                tree[deep + 1][ls++] = tree[deep][i];
                if (tree[deep][i] == sorted[mid])
                    same--;
            } else {
                tree[deep + 1][rs++] = tree[deep][i];
            }
            toleft[deep][i] = toleft[deep][i - 1]+flag;
        }
        build_tree(left, mid, deep + 1);
        build_tree(mid + 1, right, deep + 1);
    }
    int query(int left, int right, int k, int L, int R, int deep)
    {
        if (left == right)
            return tree[deep][left];
        int mid = (L + R) >> 1;
        int x = toleft[deep][left - 1] - toleft[deep][L - 1];//位于left左边的放于左子树中的数字个数
        int y = toleft[deep][right] - toleft[deep][L - 1];//到right为止位于左子树的个数
        int ry = right - L - y;//到right右边为止位于右子树的数字个数
        int cnt = y - x;//[left,right]区间内放到左子树中的个数
        int rx = left - L - x;//left左边放在右子树中的数字个数
        if (cnt >= k) {
            //printf("sss %d %d %d
    ", xx++, x, y);
            return query(L + x, L + y - 1, k, L, mid, deep + 1);
        }
        else {
            //printf("qqq %d %d %d
    ", xx++, x, y);
            return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, deep + 1);
        }
    }
    
    
    int main() {
        int n, m;
        while(scanf("%d%d", &n, &m) == 2){
            for(int i=1; i<=n; i++) {
                scanf("%d", &sorted[i]);
                tree[0][i] = sorted[i];
            }
            sort(sorted+1, sorted+1+n);
            build_tree(1, n, 0);
            for(int i=0; i<m; i++){
                int a, b, k;
                scanf("%d%d%d", &a, &b, &k);
                printf("%d
    ", query(a, b, k, 1, n, 0));
            }
        }
    
        return 0;
    }
  • 相关阅读:
    2020软件工程作业02
    2020软件工程作业01
    为什么需要平衡二叉树?
    手机号码和邮箱等联系地址,为什么不明文显示?
    请把重要的事看轻 ——2017年终总结
    万事皆空:随缘而定
    微服务:微服务架构模式译文说明
    Mysql 查询—按位运算
    解决:spring security 登录页停留时间过长 跳转至 403页面
    excel模板解析—桥接模式:分离解析模板和业务校验
  • 原文地址:https://www.cnblogs.com/xingxing1024/p/5459819.html
Copyright © 2011-2022 走看看