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

Super Mario

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 9714    Accepted Submission(s): 4113


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
 
题意:给出n个数组成的无序序列,序列中的每个数表示一块砖的高度,有m个查询,每个查询三个数,前两个数表示序列的一个子区间,第三个数表示马里奥能跳到的最大高度,问你在这个子区间中,马里奥共能跳过多少个砖头。
题解:我们可以这么想,对于一个子区间,我们将它按从小到大排好序,然后对这个区间进行二分,找到这个区间排序后的中间值,若马里奥能跳过的最大高度大于中间值,则可得知马里奥可以跳过排序后区间的前半段,然后对区间后边段进行二分;若最大高度小于中间值,则对排序后区间前半部分进行二分,一直持续二分下去,直到结束。最终二分的停止位置就是可以被跳过的砖的数量。
  而每次查询都对子区间进行排序的话时间复杂度太高,所以我们可以用划分树对区间进行维护。划分树的作用就是查找区间当中的第k大的值,这里我就不讲解划分树了,不会的可以先去学一学。每次二分都用划分树找出当前的第mid个值,然后与马里奥能跳过的最大值进行比较。
 
具体看代码:
  1 #include<iostream>
  2 #include<iomanip>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<string>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<fstream>
  9 #include<stack>
 10 #include<climits>
 11 #include<queue>
 12 #define eps 1e-7
 13 //#define ll long long
 14 #define inf 0x3f3f3f3f
 15 #define pi 3.141592653589793238462643383279
 16 using namespace std;
 17 const int MAXN = 1e5 + 7;
 18 int n,m,tree[20][MAXN],sorted[MAXN],toleft[20][MAXN];
 19 
 20 void build(int l,int r,int dep)  //建立划分树 
 21 {
 22     if(l == r) return;
 23     
 24     int mid = (l + r)>>1;
 25     int lpos = l;
 26     int rpos = mid + 1;
 27     int same = mid - l + 1;
 28     for(int i=l; i<=r; ++i)
 29         if(tree[dep][i] < sorted[mid])
 30             same--;
 31     
 32     for(int i=l; i<=r; ++i)
 33     {
 34         if(tree[dep][i] < sorted[mid])
 35             tree[dep+1][lpos++] = tree[dep][i];
 36         else if(tree[dep][i] == sorted[mid] && same > 0)
 37         {
 38             tree[dep+1][lpos++] = tree[dep][i];
 39             same--;
 40         }
 41         else tree[dep+1][rpos++] = tree[dep][i];
 42         toleft[dep][i] = toleft[dep][l-1] + lpos - l;
 43     }
 44     
 45     build(l,mid,dep+1);
 46     build(mid+1,r,dep+1);
 47 }
 48 
 49 int query(int l,int r,int L,int R,int dep,int k)  //划分树查询区间第k大值 
 50 {
 51     if(l == r)
 52         return tree[dep][l];
 53     int mid = (L + R) >> 1;
 54     int cnt = toleft[dep][r] - toleft[dep][l-1];
 55     
 56     int newl,newr;
 57     if(cnt >= k)
 58     {
 59         newl = L + toleft[dep][l-1] - toleft[dep][L-1];
 60         newr = newl + cnt - 1;
 61         return query(newl,newr,L,mid,dep+1,k);
 62     }
 63     else
 64     {
 65         newr = r + (toleft[dep][R] - toleft[dep][r]);
 66         newl = newr - r + l + cnt;
 67         return query(newl,newr,mid+1,R,dep+1,k - cnt);
 68     }
 69 }
 70 
 71 int solve(int ll,int rr,int h)    //计算区间当中可以被跳过的砖头的数量 
 72 {
 73     int l = 1,r = (rr-ll) + 1,ans=0; 
 74     while(l <= r)
 75     {
 76         int mid = (l + r) >> 1;  //取当前区间的中间值下标 
 77         if(query(ll,rr,1,n,0,mid) <= h)  //划分树查询得到中间值,若中间值 <= h 
 78         {
 79             l = mid + 1; //缩小区间 
 80             ans = mid;
 81         }
 82         else r = mid - 1;
 83     }
 84     return ans;
 85 }
 86 
 87 int main()
 88 {
 89     int t,cnt = 0;
 90     cin>>t;
 91     while(t--)
 92     {
 93         cin>>n>>m;
 94         for(int i=1; i<=n; ++i)
 95         {
 96             scanf("%d",&sorted[i]);
 97             tree[0][i] = sorted[i];
 98         }
 99         sort(sorted+1, sorted+1+n);
100         build(1,n,0);
101         
102         printf("Case %d:\n",++cnt);
103         int l,r,h;
104         while(m--)
105         {
106             scanf("%d%d%d",&l,&r,&h);
107             printf("%d\n",solve(l+1,r+1,h));
108         }
109     }
110     return 0;
111 }

 

posted @ 2018-11-23 21:22  特务依昂  阅读(247)  评论(0编辑  收藏  举报