题意:给定一个n个数的数组,有m个查询,每个查询为[a,b,k],意思是区间[a,b]的第k大数。
思路:由这个题认识了什么叫做划分树。划分树的基本思想就是对于某个区间,把它划分成两个子区间,左边区间的数小于右边区间的数。查找的时候通过记录进入左子树的数的个数,确定下一个查找区间,最后范围缩小到1,就找到了。
建树的过程比较简单,对于区间[l,r],首先通过对原数组的排序找到这个区间的中位数a[mid],小于a[mid]的数划入他的左子树[l,mid-1],大于它的划入右子树[mid,r]。同时,对于第i个数,记录在[l,i]区间内有多少数被划入左子树。最后,对它的左子树区间[l,mid-1]和右子树区间[mid,r]递归的继续建树就可以了。
建树的时候要注意对于被分到同一子树的元素,元素间的相对位置不能改变。
查找的过程:查找深度为h,在大区间[L,R]中找小区间[left,right]中的第k元素。再看看他是如何工作的。我们的想法是,先判断[left,right]中第k元素在[L,R]的哪个子树中,然后找出对应的小区间和k,递归的进行查找,直到小区间的left = right为止。具体思路见代码中注释。
注意:num数组除了这种建立方法还可以在建树时以每个区间为单位建立(百度百科的方法),
if( i==l )这样建树在查询的时候代码能少写一些。
#include <cstdio> #include <algorithm> #define N 100005 using namespace std; int s[N],t[25][N],num[25][N]; int n,m; void build(int left,int right,int d){ int i,j,k,mid,ml,flag; if(left == right) return; mid = (left+right)>>1; ml = mid-left+1; for(i = left;i<=right;i++)//注意是从left到right而不是到mid if(t[d][i] < s[mid]) ml--; j = left,k = mid+1; for(i = left;i<=right;i++){ flag = 0; if(t[d][i] < s[mid] || (t[d][i]==s[mid]&&ml)){//表示进入左子树 flag = 1; if(t[d][i] == s[mid])//如果是和中位数相等的情况 ml--; t[d+1][j++] = t[d][i]; }else t[d+1][k++] = t[d][i]; num[d][i] = num[d][i-1]+flag;//记录进入左子树的个数 } build(left, mid, d+1); build(mid+1, right, d+1); } int query(int left,int right,int k,int L,int R,int d){ int mid,x,y,rx,ry,val; if(left == right) return t[d][left]; mid = (L+R)>>1; x = num[d][left-1] - num[d][L-1];//[L,left)内去往左子树的个数 y = num[d][right] - num[d][L-1];//[L,right]内去往左子树的个数 val = y-x;//[left,right]内去往左子树的个数 rx = left - L - x;//[L,left)内去往右子树的个数 ry = right - L + 1 - y;//[L,right]内去往右子树的个数 if(k <= val) return query(L+x, L+y-1, k, L, mid, d+1); return query(mid+1+rx, mid+ry, k-val, mid+1, R, d+1); } int main(){ int i,j,k,x; scanf("%d %d",&n,&m); for(i = 1;i<=n;i++){ scanf("%d",&s[i]); t[0][i] = s[i]; } sort(s+1,s+1+n); build(1,n,0); for(i = 0;i<m;i++){ scanf("%d %d %d",&j,&k,&x); printf("%d\n",query(j,k,x,1,n,0)); } } /* t数组值如下,四周序号表示下标 t 1 2 3 4 5 6 7 0 1 5 2 6 3 7 4 1 1 2 3 4 5 6 7 2 1 2 3 4 5 6 7 3 1 2 3 4 5 6 0 num数组值如下,四周序号表示下标 num 1 2 3 4 5 6 7 0 1 1 2 2 3 3 4 1 1 2 2 2 3 4 4 2 1 1 2 2 3 3 0 */