• HDU-2665(模板题 划分树 || 主席树)


    题目链接

    题意: 求区间第K大

    划分树

    划分树好像只能用于求这个

    划分树,顾名思义是将n 个数的序列不断划分,根结点就是原序列,左孩子保存父结点所有元素排序后的一半,右孩子也存一半,也就是说排名1 -> mid的存在左边,排名(mid+1) -> r 的存在右边,同一结点上每个元素保持原序列中相对的顺序
    同时,用一个标记数组记录,每次划分时,当前位置累积被分配到左子树的个数,查询时就判断该区间第k大是被划分到了左子树,还是右子树,直到找到。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    const int maxn = 100050;
    using namespace std;
    int tree[20][maxn];
    int tsort[maxn];
    int toleft[20][maxn];
    int n, m;
    
    void build(int l, int r, int deep){
        if(l == r)  //到达叶子
            return ;
        int m = (l+r)>>1;
        int same = m - l + 1; //得到应该被划分到左子树的个数  但same是用来表示,与中位数相等但是应该被划分去左子树的个数
        for(int i=l; i<=r; i++)         //求出真正的same
            if(tree[deep][i] < tsort[m])
                same--;
        int lpos=l, rpos=m+1;       //左右子树的起点
        for(int i=l; i<=r; i++){
            if(tree[deep][i]<tsort[m])      //划分到左子树
                tree[deep+1][lpos++] = tree[deep][i];
            else{
                if(tree[deep][i]==tsort[m] && same)     //与中位数相等,但是要划分到左子树
                    tree[deep+1][lpos++] = tree[deep][i], same--;
                else
                    tree[deep+1][rpos++] = tree[deep][i];       //划分右子树
            }
            toleft[deep][i] = toleft[deep][l-1]+(lpos-l);   //计算当前位置划分到左子树的个数
        }
        build(l, m, deep+1);
        build(m+1, r, deep+1);
    }
    
    int query(int l, int r, int deep, int L, int R, int k){
        //printf("%d %d %d %d %d****
    ", l, r, L, R, k);
        if(l == r)
            return tree[deep][l];
    
        int m = (l+r)>>1;
        int cnt = toleft[deep][R] - toleft[deep][L-1];   //被划分到左子树的个数
        //printf("%d %d###
    ", m, cnt);
        //newL newR newk 是被划分到左子树(或右子树),在子树中的新区间
        if(cnt>=k){
            int newL = l + toleft[deep][L-1] - toleft[deep][l-1];
            int newR = newL + cnt - 1;
            return query(l, m, deep+1, newL, newR, k);
        }
        else{
            int newR=R+toleft[deep][r]-toleft[deep][R];
            int newL=newR-(R-L-cnt);
            int newk = k-cnt;
            return query(m+1, r, deep+1, newL, newR, newk);
        }
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--){
            scanf("%d%d", &n, &m);
            for(int i=1; i<=n; i++){
                scanf("%d", &tree[0][i]);
                tsort[i] = tree[0][i];
            }
            sort(tsort+1, tsort+n+1);
            build(1, n, 0);
    
            /*
            for(int i=0; i<=3; i++){
                for(int j=1; j<=n; j++){
                    printf("%d ", tree[i][j]);
                }
                printf("******
    ");
            }
            printf("
    
    
    
    ");
            for(int i=0; i<=3; i++){
                for(int j=1; j<=n; j++){
                    printf("%d ", toleft[i][j]);
                }
                printf("******
    ");
            }
            */
    
            //printf("####
    ");
            while(m--){
                int a, b, k;
                scanf("%d%d%d", &a, &b, &k);
                printf("%d
    ", query(1, n, 0, a, b, k));
            }
        }
        return 0;
    }
    
    /*
    7 3
    1 5 2 6 3 7 4
    2 5 3
    4 4 1
    1 7 3
     */
    

    主席树

    #include <bits/stdc++.h>
    const int maxn = 100050;
    using namespace std;
    typedef long long ll;
    
    int a[maxn], b[maxn], rt[maxn*20], ls[maxn*20], rs[maxn*20], sum[maxn*20];
    // rt[i] 第i个版本的根节点所在的位置
    // ls[i] rs[i] 节点的左右子树的位置
    int n, m, tot, len;
    
    void build(int &o, int l, int r){       //确定最初的版本
        o = ++tot;          //这里 tot 的统计是用的dfs(先序)
        sum[o] = 0;
        if(l == r)
            return ;
        int m = (l+r)>>1;
        build(ls[o], l, m);
        build(rs[o], m+1, r);
    }
    
    void update(int &o, int l, int r, int last, int p){
        o = tot++;
        ls[o] = ls[last];
        rs[o] = rs[last];//先继承前个版本的左右子树
        sum[o] = sum[last]+1;
        if(l == r)
            return ;
        int m = (l+r)>>1;
        if(p<=m)        //更新左右子树
            update(ls[o], l, m, ls[last], p);
        else
            update(rs[o], m+1, r, rs[last], p);
    }
    
    int query(int ss, int tt, int l, int r, int k){
        if(l == r)
            return l;
        int m = (l+r)>>1;
        int cnt = sum[ls[tt]] - sum[ls[ss]];        //两个版本中左子树的相差的个数
        if(cnt >= k)        //答案在左子树中
            return query(ls[ss], ls[tt], l, m, k);
        else    //答案在右子树中
            return query(rs[ss], rs[tt], m+1, r, k-cnt);
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--){
            scanf("%d%d", &n, &m);
            for(int i=1; i<=n; i++){
                scanf("%d", &a[i]);
                b[i] = a[i];
            }
            sort(b+1, b+n+1);
            tot = 0;
            len = unique(b+1, b+n+1) - (b+1);   //要离散化的去重数组
            build(rt[0], 1, len);
            for(int i=1; i<=n; i++){
                a[i] = lower_bound(b+1, b+len+1, a[i]) - b;
                update(rt[i], 1, len, rt[i-1], a[i]);
            }
            while(m--){
                int l, r, k;
                scanf("%d%d%d", &l, &r, &k);
                int ans = query(rt[l-1], rt[r], 1, len, k);
                printf("%d
    ", b[ans]);
            }
        }
        return 0;
    }
    
    /*
     * 在这里主席树其实相当于多颗权值线段树
     * 由于每次版本的更新只会更新一个点,所以对于每个点,只会有一个子节点会发生更新
     * 主席树就是重复利用了不会更新的节点,只去创建会更新的节点
     */
    
  • 相关阅读:
    <--------------------------常用的API方法------------------------------>
    <--------------------------构造方法------------------------------>
    <--------------------------Java多态如何使用------------------------------>
    <--------------------------Java接口如何使用------------------------------>
    <--------------------------Java继承及抽象类------------------------------>
    Eclipse工具的设置
    Java数组逆序排列
    Java打印九九乘法表及倒打九九乘法表
    Java打印水仙花数
    Java实现选择排序以及冒泡排序
  • 原文地址:https://www.cnblogs.com/jizhihong/p/13337356.html
Copyright © 2020-2023  润新知