Time Limit: 20000MS | Memory Limit: 65536K | |
Total Submissions: 45653 | Accepted: 15177 | |
Case Time Limit: 2000MS |
Description
Input
Output
Sample Input
7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3
Sample Output
5 6 3
转自:http://blog.csdn.net/famousdt/article/details/7064866
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入它的左子树[l,mid-1],大于它的划入右子树[mid,r]。
同时,对于第i个数a[i],记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
建树的时候要注意,对于被分到同一子树的元素,元素间的相对位置不能改变。
查找的过程中主要问题就是确定将要查找的区间。
查找深度为dep,在大区间[L ,R]中找小区间[l ,r]中的第k元素。
我们的想法是,先判断[l ,r]中第k元素在[L ,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的l==r为止。
通过之前的记录可以知道,在区间[L,l-1]中有(toleft[dep][l-1]-toleft[dep][L-1])进入左子树,
记它为x。
同理区间[L,r]中有(toleft[dep][r]-toleft[dep][L-1])个数进去左子树,记它为y。
所以,我们知道区间小区间[l,r]中有(y-x)个数进入左子树。那么如果(y-x)>=k,那么就在左子树中继续查找,否则就在右子树中继续查找。
接着,解决查找的小区间的问题。
如果接下来要查找的是左子树,那么小区间应该是[L+([L,l-1]区间进入左子树的个数),L+([L,r]区间内进入左子树的个数)-1]。即区间[L+x,L+y-1]。(这里的-1应该是防止边界问题)
显然,这里k不用变。
如果接下来要查找的是右子树,那么小区间应该是[mid+([L,l-1]区间中进入右子树的个数),mid+([L,r]区间进入右子树的个数)-1]。
即区间[mid+(l-L-x),mid+(r-L-y)]。(这里要记得+1)
显然,这里k要减去区间里已经进入左子树的个数,即k变为k-(y-x)。
#include <stdio.h> #include <algorithm> #include <string.h> using namespace std; const int MAXSIZE = 100100; int tree[30][MAXSIZE]; ///树最多也就30层 int _sort[MAXSIZE]; int toleft[30][MAXSIZE]; void build(int l,int r,int deep) { if(l==r) return; int mid =(l+r)>>1; int same = mid-l+1; ///设这个变量的主要原因是判断tree[deep][i]==_sort[mid]时tree[deep][i]往哪边走 for(int i=l; i<=r; i++)///计算放于左子树中与中位数相等的数字个数, { // printf("%d ",tree[deep][i]); if(tree[deep][i]<_sort[mid]) same--; } //printf("\n"); int left = l; int right = mid+1; for(int i=l; i<=r; i++) { int flag = 0; if((tree[deep][i]<_sort[mid])||(tree[deep][i]==_sort[mid]&&same>0)) { flag = 1; tree[deep+1][left++] = tree[deep][i]; if(tree[deep][i]==_sort[mid]) same--; } else { tree[deep+1][right++] = tree[deep][i]; } toleft[deep][i] = toleft[deep][i-1]+flag; } build(l,mid,deep+1); build(mid+1,r,deep+1); } int query(int L,int R,int l,int r,int k,int deep) { if(l==r) return tree[deep][l]; int x = toleft[deep][l-1]-toleft[deep][L-1]; ///x代表[L,l-1]区间内进入左子树的数字的量 int y = toleft[deep][r]- toleft[deep][L-1]; ///y代表[L,r]区间内进入左子树的数字的量 int cnt = y-x; ///cnt代表[l,r]区间内进入左子树的数字的量 int mid = (L+R)>>1; int rx = l - L - x;///l左边放在右子树中的数字个数 int ry = r - L - y;///到r右边为止位于右子树的数字个数 if(cnt>=k) { query(L,mid,L+x,L+y-1,k,deep+1); }else{ query(mid+1,R,mid+rx+1,mid+ry+1,k-cnt,deep+1); } } int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { memset(toleft,0,sizeof(toleft)); for(int i=1; i<=n; i++) { scanf("%d",&_sort[i]); tree[0][i] = _sort[i]; } sort(_sort+1,_sort+n+1); build(1,n,0); while(m--) { int a,b,k; scanf("%d%d%d",&a,&b,&k); int t = query(1,n,a,b,k,0); printf("%d\n",t); } } return 0; }