zoukankan      html  css  js  c++  java
  • 划分树---hdu4417---区间查找(不)大于h的个数


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


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

    today lazy . tomorrow die .
  • 相关阅读:
    比特币脚本及交易分析
    分析比特币网络:一种去中心化、点对点的网络架构
    比特币如何挖矿(挖矿原理)-工作量证明
    比特币所有权及隐私问题-非对称加密应用
    区块链记账原理
    用Python从零开始创建区块链
    基础语法-算术运算符
    基础语法-数据类型转换
    基础语法-数据类型介绍及使用
    基础语法-变量介绍
  • 原文地址:https://www.cnblogs.com/france/p/4808707.html
Copyright © 2011-2022 走看看