Super Mario
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 120 Accepted Submission(s): 73
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
Source
2012 ACM/ICPC Asia Regional Hangzhou Online
Recommend liuyiding
此题比赛的时候想得比较久。
比赛是离线处理,用树状数组或者线段树做的。。。
因为一开始用树状数组写错一个地方,死循环了,一直TLE。所以换线段树写了下,还是TLE。
后来马上发现错误,改正过后马上AC了。速度还是比较快的。
树状数组或者线段树的题解见下面:
http://www.cnblogs.com/kuangbin/archive/2012/09/23/2699122.html
晚上回来,突然想到划分树了。其实只要用划分树二分枚举第K大,得出答案就可以的。。。。
Orz,原来这么多人快速过了都是用划分树的。。。
划分树写起来确实简单,而且不容易错。。。自己太弱了。。。
求现场赛给力啊!!!
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXN=100010; int tree[30][MAXN];//表示每层每个位置的值 int sorted[MAXN];//已经排序的数 int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边 void build(int l,int r,int dep) { if(l==r)return; int mid=(l+r)>>1; int same=mid-l+1;//表示等于中间值而且被分入左边的个数 for(int i=l;i<=r;i++) if(tree[dep][i]<sorted[mid]) same--; int lpos=l; int rpos=mid+1; for(int i=l;i<=r;i++) { if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边 tree[dep+1][lpos++]=tree[dep][i]; else if(tree[dep][i]==sorted[mid]&&same>0) { tree[dep+1][lpos++]=tree[dep][i]; same--; } else //比中间值大分入右边 tree[dep+1][rpos++]=tree[dep][i]; toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数 } build(l,mid,dep+1); build(mid+1,r,dep+1); } //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间 int query(int L,int R,int l,int r,int dep,int k) { if(l==r)return tree[dep][l]; int mid=(L+R)>>1; int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数 if(cnt>=k) { //L+要查询的区间前被放在左边的个数 int newl=L+toleft[dep][l-1]-toleft[dep][L-1]; //左端点加上查询区间会被放在左边的个数 int newr=newl+cnt-1; return query(L,mid,newl,newr,dep+1,k); } else { int newr=r+toleft[dep][R]-toleft[dep][r]; int newl=newr-(r-l-cnt); return query(mid+1,R,newl,newr,dep+1,k-cnt); } } int solve(int n,int s,int t,int h) { int ans=0; int l=1; int r=(t-s)+1; int mid; while(l<=r) { mid=(l+r)>>1; int temp=query(1,n,s,t,0,mid); if(temp<=h) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; int n,m; int s,t,h; scanf("%d",&T); int iCase=0; while(T--) { iCase++; scanf("%d%d",&n,&m); memset(tree,0,sizeof(tree));//这个必须 for(int i=1;i<=n;i++)//从1开始 { scanf("%d",&tree[0][i]); sorted[i]=tree[0][i]; } sort(sorted+1,sorted+n+1); build(1,n,0); printf("Case %d:\n",iCase); while(m--) { scanf("%d%d%d",&s,&t,&h); s++; t++; printf("%d\n",solve(n,s,t,h)); } } return 0; }