http://acm.hdu.edu.cn/showproblem.php?pid=4417

Super Mario

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2315    Accepted Submission(s): 1127

Problem Description
Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble again and Mario needs to save his lover. We regard the road to the boss’s castle as a line (the length is n), on every
integer point i there is a brick on height hi. Now the question is how many bricks in [L, R] Mario can hit if the maximal height he can jump is H.
 
Input
The first line follows an integer T, the number of test data.

For each test data:

The first line contains two integers n, m (1 <= n <=10^5, 1 <= m <= 10^5), n is the length of the road, m is the number of queries.

Next line contains n integers, the height of each brick, the range is [0, 1000000000].

Next m lines, each line contains three integers L, R,H.( 0 <= L <= R < n 0 <= H <= 1000000000.)
 
Output
For each case, output "Case X: " (X is the case number starting from 1) followed by m lines, each line contains an integer. The ith integer is the number of bricks Mario can hit for the ith query.
 
Sample Input
1
10 10
0 5 2 7 5 4 3 8 7 7
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
 
Sample Output
Case 1:
4
0
0
3
1
2
0
1
5
1
 

题目大意:在区间[s,t]查找不大于h的有多少个

做法:用划分树二分枚举第K大(具体看下面注释)

注释处有分析在区间[s,t]查找大于h的有多少个 注意融会贯通

#include <iostream>
#include <string.h>
#include <algorithm>
#define maxn 100010
#define mid ((L+R)>>1)
using namespace std;
int tree[20][maxn];//表示每层每个位置的值
int toleft[20][maxn];//20层每层maxn t用来放原序; toleft[p][i]表示第P层第i个放左节点的元素个数
int sorted[maxn];//已经排序的数
//以下为查找区间第k小划分树
void build(int p,int L,int R) //p:第几层 默认0开始 ; L,R 左右区间从[1,n]开始建
{
if(L==R) return; //这个放最上边省时
int lm=0,i,ls=L,rs=mid+1;//lm表示应被放入左子树且与中位数相等的数有多少个,ls为左子树的起始位置,rs为右子树的起始位置
for(i=mid;i>=L;i--) //求lm ;2 3 3 4 4 5 5 7 9得到的lm=2
{
if(sorted[i]==sorted[mid])
lm++; //之前有一个错误的想法: 不记录这个lm 找的时候直接找<=的 但是这样会出错 比如 2 4 4 4 5 1
else // 排序: 1 2 4 4 4 5 那如果我们直接找<=的(6/2=3)个的话 会找到 2 4 4这样就错了所以还是要记录lm
break;
}
for(i=L;i<=R;i++)
{
if(i==L)//这里要特殊讨论(原因:间接的对所有初始化 我们这样做便于toleft[p][i]=toleft[p][i-1]这一部的处理)
toleft[p][i]=0; //
else
toleft[p][i]=toleft[p][i-1];//下一个肯定是上一个+0或1 if(tree[p][i]==sorted[mid])//若与中位数相等则判断是否应该被放入左子树
{
if(lm)
{
lm--;
toleft[p][i]++; //如果满足 说明又多了一个元素放左节点了
tree[p+1][ls++]=tree[p][i];//放入下一个t[]
}
else
tree[p+1][rs++]=tree[p][i];
}
else if(tree[p][i]<sorted[mid])//查找区间第K大即为>
{
toleft[p][i]++;
tree[p+1][ls++]=tree[p][i];
}
else
tree[p+1][rs++]=tree[p][i];
}
build(p+1,L,mid);
build(p+1,mid+1,R);
}
//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int p,int L,int R,int l,int r,int k)
{
int s,ss;//s表示[L,l-1]放入左子树的个数,ss表示区间[l,r]被放入左子树的个数
if(L==R)//找到所求的数
return tree[p][L];
if(l==L)
s=0,ss=toleft[p][r];
else
s=toleft[p][l-1],ss=toleft[p][r]-s;
if(k<=ss)//要找的数在左子树中
return query(p+1,L,mid,L+s,L+toleft[p][r]-1,k);
else//要找的数在右子树中
return query(p+1,mid+1,R,mid+1-L+l-s,mid+1-L+r-toleft[p][r],k-ss);
}
//原理 在区间[s,t]查找不大于h的有多少个,我们先让区间第mid2小的数与他比较;
//如果这个数还小于h 说明现在至少有mid2个数比h小 然后找[(mid2+1)+r]>>1大的数继续比较;
//否则说明这个数比h大;不能判断至少多几个比h小;所以找[l+mid2]>>1大的数继续比较;
int solve(int L,int R,int s,int t,int h){ //在查找区间[s,t]不大于h的个数有多少[L,R]<=>[1,n]
int l=1;
int r=t-s+1;
int mid2;
int ans=0;
while(l<=r){
mid2=(l+r)>>1;
int temp=query(0,L,R,s,t,mid2);//得到区间【s,t】的第mid2小的数
/*
//如果问在查找区间[s,t]大于h的个数有多少
// 方法一:
if(temp>h){ //if第mid2小的数都比h来得大那至少有(t-s+1)-mid2+1个
ans=(t-s+1)-mid2+1;//自己画一下就能够理解了
r=mid2-1;
}
else l=mid2+1;//if第mid2小的数不大于h 往后面找
//方法2:
* query方法改成得到区间【s,t】的第mid2 ****大*****的数(上面有讲只需要改个符号即可)
if(temp>h){ //if第mid2大的数h大;那至少有mid2个数比h大
ans=mid2//
l=mid2+1;//往后找
}
else r=mid2-1;//否则无法知道至少几个,往前面找更大的数与之比较 */
if(temp<=h){
ans=mid2;
l=mid2+1;
}
else r=mid2-1; //注意这里要-1 不然答案出不来 因为while的条件是l<=r 如果不-1会出现 l=r的情况 mid2=l=r无限循环
}
return ans;
}
int main()
{
int T;
int n,m;
int s,t,h;
cin>>T;
int iCase=0;
while(T--)
{ cin>>n>>m;
for(int i=1;i<=n;i++)//从1开始
{
cin>>tree[0][i];
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(0,1,n);
cout<<"Case "<<++iCase<<":"<<endl;
while(m--)
{
cin>>s>>t>>h;
s++; //题目的输入是从[0,n-1] 我们数据转化到[1,n]
t++;
cout<<solve(1,n,s,t,h)<<endl;
}
}
return 0;
}
/*
*
input:
1
10 10
0 5 2 7 5 4 3 8 7 7
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
output:
Case 1:
4
0
0
3
1
2
0
1
5
1 * */

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

划分树---hdu4417---区间查找(不)大于h的个数的更多相关文章

  1. [csu/coj 1080]划分树求区间前k大数和

    题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数 ...

  2. 【大杀器】利用划分树秒杀区间内第k大的数

    最近看了一道题,大概就是给出一个序列,不断询问其子区间内第k大的数,下面是个截图 绕了一圈没找到中文版题目,if(你是大佬) then 去看截图:else{我来解释:给出一个整数n,和一个整数m,分别 ...

  3. HDU 3473 Minimum Sum (划分树求区间第k大带求和)(转)

    题意:在区间中找一个数,求出该区间每个数与这个数距离的总和,使其最小 找的数字是中位数(若是偶数个,则中间随便哪个都可)接着找到该区间比此数大的数的总和 区间中位数可以使用划分树,然后在其中记录:每层 ...

  4. [hdu2665]Kth number(划分树求区间第k大)

    解题关键:划分树模板题. #include<cstdio> #include<cstring> #include<algorithm> #include<cs ...

  5. HDOJ题目4417 Super Mario(划分树求区间比k小的个数+二分)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  6. hdu4417(Super Mario)—— 二分+划分树

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  7. K-query SPOJ - KQUERY 离线 线段树/树状数组 区间大于K的个数

    题意: 给一个数列,一些询问,问你区间$[l.r]$大于$K$的个数 题解: 又一个"人尽皆知傻逼题"? 我们用一个01序列表示当前询问时,该位置的数字是否对答案有贡献, 显然,对 ...

  8. HDU 4417 Super Mario (划分树)(二分)

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. hdu 4417,poj 2104 划分树(模版)归并树(模版)

    这次是彻底把划分树搞明确了,与此同一时候发现了模版的重要性.敲代码一个字符都不能错啊~~~ 划分树具体解释:点击打开链接 题意:求一组数列中随意区间不大于h的个数. 这个题的做法是用二分查询  求给定 ...

随机推荐

  1. 浅淡 RxJS WebSocket

    const open$ = new Subject(); const ws = webSocket({ url: 'wss://echo.websocket.org', openObserver: o ...

  2. 学习python的一些脚本

    python生产者与消费者模型 # -*- coding:utf-8 -*- import threading import random import time gMoney = 1000 gLoc ...

  3. (杭电1019 最小公倍数) Least Common Multiple

    Least Common Multiple Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...

  4. 关于typedef在struct使用上的一些问题

    typedef struct lnode{ int data; struct lnode next; }lnode,linklist; 第一行的lnode是结构体名,最后一行的lnode是由typed ...

  5. python的MetaClass的代码分析。基于廖雪峰博客代码

    # 一张表一个类,表内每一行就是一个实例 ''' 一个单独的元类使用的程序分析. ''' class Field(object): def __init__(self, name, column_ty ...

  6. Java六大设计原则

    类的设计原则     依赖倒置原则-Dependency Inversion Principle (DIP) 里氏替换原则-Liskov Substitution Principle (LSP) 接口 ...

  7. QT-2D编程

    QT-[转]2D编程 Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕上和绘图·设备上进行绘制,主要基于QPainter.QPainterDevice和QPainterEngine这3个类. ...

  8. 人脸识别引擎SeetaFaceEngine中Identification模块使用的测试代码

    人脸识别引擎SeetaFaceEngine中Identification模块用于比较两幅人脸图像的相似度,以下是测试代码: int test_recognize() { const std::stri ...

  9. SOAPUI参数中xml中CDATA包含问题

    <![CDATA[ <Request> <CardNo>000002629518</CardNo> <SecrityNo/> <BankTr ...

  10. Struts 2(四):类型转换

    类型转换是Struts 2的一个非常重要的部分,通过类型转换能够将表单参数转换成Java中的各种类型,本文将详细介绍Struts 2的内建类型转换器和自定义类型转换器. 第一节 Struts 2内建类 ...