hdu 2665 划分树

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <stack>
#include <set>
#include <map>
#include <vector>

using namespace std;
#define INF 0x2fffffff
#define LL long long
#define MAX(a,b) ((a)>(b))?(a):(b)
#define MIN(a,b) ((a)<(b))?(a):(b)
#define maxn 100010
#define mid ((l+r)>>1)
int n,m;
int tree[30][maxn];
int toleft[30][maxn];
int so[maxn];
void biuld(int p,int l,int r){
    if(l == r) return;
    int lm = 0;
    for(int i = mid;i >= l;i--){
        if(so[i] == so[mid]) lm++;
        else break;
    }

    int ls = l;
    int rs = mid+1;
    for(int i = l;i <= r;i++){
        if(i==l)
            toleft[p][i] = 0;
        else
            toleft[p][i] = toleft[p][i-1];
        if(tree[p][i] == so[mid]){
            if(lm){
                lm --;
                toleft[p][i]++;
                tree[p+1][ls++] = tree[p][i];
            }
            else{
                tree[p+1][rs++] = tree[p][i];
            }
        }
        else if(tree[p][i] < so[mid]){
            toleft[p][i] ++;
            tree[p+1][ls++] = tree[p][i];
        }
        else {
            tree[p+1][rs++] = tree[p][i];
        }
    }
    biuld(p+1,l,mid);
    biuld(p+1,mid+1,r);
}
int query(int p,int l,int r,int x,int y,int k){
    if(l == r) return tree[p][l];
    int s;
    int ss;
    if(x == l){
        s = 0;
        ss = toleft[p][y];
    }
    else{
        s = toleft[p][x-1];
        ss = toleft[p][y] - s;
    }
    if(k <= ss){
        return query(p+1,l,mid,l+s,l+toleft[p][y]-1,k);//减一的原因是toleft[p][r] 中包含了l所以要减去一个l开始的位置,这样才有ss大小的区间
    }
    else{
        return query(p+1,mid+1,r,mid+1+x-l-s,mid+1+y-l-toleft[p][y],k-ss); //放入左子树的是s则放入右子树的是x-l-s,
    }
}
int main(){
    int t;
    cin >> t;
    while(t--){
        scanf("%d%d",&n,&m);
        memset(tree,0,sizeof(tree));
        memset(toleft,0,sizeof(toleft));
        for(int i = 1;i <= n;i++){
            scanf("%d",&tree[0][i]);
            so[i] = tree[0][i];
        }
        sort(so+1,so+1+n);
        biuld(0,1,n);
        for(int i = 0; i < m;i++){
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            printf("%d\n",query(0,1,n,x,y,k));
        }
    }
    return 0;
}

划分树的思想其实是将快排的步骤记录下来,然后依据快排过程中的信息求每一个区间的kth数

划分树里面最重要的是对toleft这个数组的理解,其实就是在这个toleft[p][i] 是i到i的所在的树的起点中放到左子树的数的个数这时候就跟二分找第k大的数的时候的思想差不多了

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 05:23:20

hdu 2665 划分树的相关文章

hdu 2665 划分树模板题(可作为模板)

Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6951    Accepted Submission(s): 2214 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The fi

HDU 4417 划分树+二分

题意:有n个数,m个询问(l,r,k),问在区间[l,r] 有多少个数小于等于k. 划分树--查找区间第k大的数.... 利用划分树的性质,二分查找在区间[l,r]小于等于k的个数. 如果在区间第 i 大的数tmp>k,则往下找,如果tmp<k,往上找. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #incl

HDU 4417 (划分树+区间小于k统计)

题目链接:  http://acm.hdu.edu.cn/showproblem.php?pid=4417 题目大意:给定一个区间,以及一个k值,求该区间内小于等于k值的数的个数.注意区间是从0开始的. 解题思路: 首先这题线段树可以解.方法是维护一个区间最大值max,一个区间点个数s,如果k>max,则ans=s+Q(rson),否则ans=Q(lson). 然后也可以用求区间第K大的划分树来解决,在对原来求第K大的基础上改改就行,方法如下: Build方法同第K大. 对于Query: ①左区

hdu 3473 划分树 ***

题目大意:有一个数列 x1..xn,要求一个数x使得 sigma(abs(xi-x))值最小,很明显,对数列进行排序后最中间的那个数就是x,可用划分树求得,那么如何求和呢,经过计算可知,既然 x 是最中间的那个数,那么最后的和 即为 x左边 xmid-x1+xmid-x2.. +  x(mid+1) - xmid + x(mid+2)-xmid..  整理得 xmid*(lefnum-rignum)+rigsum-lefsum lefnum为划分过程进入左子树的个数,lefsum为进入左子树的数

HDU 2665(主席树,无修改第k小)

Kth number                                                 Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                                                        Total Submission(s): 10682    Accep

hdu 2665 Kth number(划分树)

Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4602 Accepted Submission(s): 1468 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first l

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

hdu 2665 Kth number(划分树)

题意:给定一列数,每次查询区间[s,t]中的第k大: 参考:http://www.cnblogs.com/kane0526/archive/2013/04/20/3033212.html http://www.cnblogs.com/kuangbin/archive/2012/08/14/2638829.html 思路:快排思想+线段树=划分树,也就是树的每一层都按规则划分: 对于本题,建树前,保存原数列排序后的数列,原数列作为树的顶层: 建树时,考虑当前区间中的中间值,若大于中间值,在下一层中

hdu 2665 Kth number (poj 2104 K-th Number) 划分树

划分树的基本功能是,对一个给定的数组,求区间[l,r]内的第k大(小)数. 划分树的基本思想是分治,每次查询复杂度为O(log(n)),n是数组规模. 具体原理见http://baike.baidu.com/link?url=vIUKtsKYx7byeS2KCOHUI14bt_0sdHAa9BA1VceHdGsTv5jVq36SfZgBKdaHYUGqIGvIGrE_aJtqy0D0b1fCoq 个人感觉看代码是最好的学习方法. #include <cstdio> #include <c