划分树(poj2104)

poj2104

题意:给出n个数,有m次查询,每次查询要你找出 l 到 r 中第 k 大的数;

思路:划分树模板题

上述图片展现了查询时如何往下递推的过程

其中ly表示 [sl,l) 中有多少个数进入了左子树,num[ceng][r]表示[sl,r]中有多少个数进入了左子树,total表示[l,r]中有多少个数进入了左子树。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int s[20][100010];
int st[100010];//排序后的数组
int num[20][100010];//第i层前j个数有几个进入了左子树
void bt(int ceng,int l,int r){
    if(l==r)//递归到l,r相等时
    return;
    int mid=(l+r)/2;
    int sum1=mid-l+1;
    for(int i=l;i<=r;i++){//计算该有多少个与中间值相等的数可以进入左子树
        if(s[ceng][i]<st[mid])
        sum1--;
    }
    int cnt1=0,cnt2=1;
    for(int i=l;i<=r;i++){
        if(i==l){
            num[ceng][i]=0;
        }
        else{
            num[ceng][i]=num[ceng][i-1];
        }

        if(s[ceng][i]<st[mid]||s[ceng][i]==st[mid]&&sum1>0){//如果当前数字小于中间数或者当前数等于中间数并且当前进入左子树并与中间数相等的数的数量小于限制数量时
            int k1=l+cnt1++;
            //printf("qqqq%d %d %d\n",ceng,cnt1,k1);
            s[ceng+1][k1]=s[ceng][i];
            num[ceng][i]++;
            if(s[ceng][i]==st[mid])//如果相等,则与中间数相等的数可以进入的位置又少了一个
            sum1--;
        }
        else{//进入右子树
            int k2=mid+cnt2++;
            s[ceng+1][k2]=s[ceng][i];
            //printf("qqqq%d %d %d\n",ceng,cnt2,k2);
        }
    }
    bt(ceng+1,l,mid);//递归建树
    bt(ceng+1,mid+1,r);
}
int query(int ceng,int sl,int sr,int l,int r,int k){
    //printf("www%d %d %d\n",ceng,sl,sr);
    if(sl==sr){//递归到叶子节点
        //printf("qq%d %d %d\n",ceng,sl,s[ceng][sl]);
        return s[ceng][sl];
    }
    int ly;
    if(l==sl)
    ly=0;//ly代表该段的l前面有多少个数进入了左子树
    else
    ly=num[ceng][l-1];
    int total=num[ceng][r]-ly;//l到r之间有多少个数进入了左子树
    if(total>=k){//该区间有大于k个数进入了左子树 ,则第k大的数一定在左子树里面
        return query(ceng+1,sl,(sl+sr)>>1,sl+ly,sl+num[ceng][r]-1,k);
        //l=sl+ly;新的左范围等于边界sl+l前面的数进入左子树的个数
        //r=sl+num[ceng][r]-1;新的右范围等于边界sl+前r个数中进入左子树的个数
    } //为什么r!=sl+ly +  k因为虽然连续,但不是有序的
    else{
        int lr=l-sl-ly+((sl+sr)>>1)+1;//新的左范围等于l前面的数的总数减去前面数进入左子树的个数加上右子数的开始位置
        return query(ceng+1,((sl+sr)>>1)+1,sr,lr,lr+r-l-total,k-total);
        //新的右范围等于新的左范围加上l到r之间数的个数减去l和r之间的数进入左子树的个数
    }

}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&s[0][i]);
        st[i]=s[0][i];
    }
    sort(st+1,st+n+1);
    bt(0,1,n);
    while(m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query(0,1,n,l,r,k));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cglongge/p/10035749.html

时间: 2024-10-10 21:24:48

划分树(poj2104)的相关文章

划分树 poj2104 hdu5249

KPI Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 616    Accepted Submission(s): 261 Problem Description 你工作以后, KPI 就是你的所有了. 我开发了一个服务.取得了非常大的知名度.数十亿的请求被推到一个大管道后同一时候服务从管头拉取请求.让我们来定义每一个请求都有一个重要

poj2104 划分树 区间K大 在线 无修改

博主sbit....对于高级数据结构深感无力,然后这些东西在OI竟然烂大街了,不搞就整个人都不好了呢. 于是我勇猛的跳进了这个大坑 ——sbit 区间K大的裸题,在线,无修改. 可以用归并树(\(O(nlog^3n)\)),也可用划分树(\(O(nlogn + mlogn)\)).果断划分树...(以后再来看归并树...再来看...来看..看..) 划分树是个什么东西呢?为什么可以做区间k大呢? 想想平衡树做k大时是如何搞的,其实内在原理是一样的. 划分树分两个步骤:建树与询问,这两个步骤相互关

划分树 静态第k大

划分树是保存了快速排序的过程的树,可以用来求静态第k小的数 如果,划分树可以看做是线段树,它的左孩子保存了mid-L+1 个 小于等于 a[mid] 的数字,  右孩子保存了 R-mid个大于等于a[mid]的数字 数组a是排序过后的数组,而划分树保存的是原数组的数据, 划分树的构造就是将上一层[l,r]个数的 mid-l+1个数划分到左子区间,r-(mid-l+1)个数划分到了右子区间 void build(int l, int r, int rt) { if (l == r) return;

杭电ACM2665——Kth number~~划分树

题目的意思:给点区间[a, b],查找第K大的数,和POJ2104题一样,只是HDU上的时间限制5000MS,用我在POJ上的方法,过不了,会超时. 而这一题的代码,改一下main函数的输入,就可以直接AC了POJ上的2104. 这题,用分桶法,WR,纠结了一晚上,最后还是放弃了,实在不知道错在哪里.于是改用了划分树的方法,学习了划分树的建立和查找. 划分树:主要运用于求解序列中区间[a, b]上的第K大的数,也就是区间[a, b]从小到大排序,第K个. 主要的算法思路是: 1,建树:先排序好存

划分树基础知识

原网址:划分树详解 对4 5 2 8 7 6 1 3 分别建划分树和归并树 划分树如下图 红色的点是此节点中被划分到左子树的点. 我们一般用一个结构体数组来保存每个节点,和线段树不同的是,线段树每个节点值保存一段的起始位置和结束位置,而在划分树和递归树中,每个节点的每个元素都是要保存的.为了直观些,我们可以定义一个结构体数组,一个结构体中保存的是一层的元素和到某个节点进入左子树的元素的个数,不同于线段树,我们不能保存一个节点的起始结尾位置,因为随层数的增加,虽然每个结构体保存的元素数目是一定的,

hdu-4417 Super Mario(树状数组 + 划分树)

题目链接: Super Mario Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/Others) Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is

HDU 3473 Minimum Sum 划分树,数据结构 难度:1

http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.net/shiqi_614/article/details/8041390 划分树的定义 划分树定义为,它的每一个节点保存区间[lft,rht]所有元素,元素顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后(rht-lft+1)/2个进入左子树,其余的到右子树,同时维护一个num域,

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 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