我感觉划分树的基本思想是二分和归并排序,分为建树和查询两个部分。
1、建树
递归建树,以中值为界,将序列划分成左右两部分,直到分到每个点为止。同时,在建树的过程中,记录下每一层进入左区间的数的个数,方便查询时使用。
注意:保持左右区间中数的相对顺序。
(1)、中值唯一的情况。将小于等于中值的数放到左区间即可。
(2)、中值不唯一的情况。为了防止左区间放了过多的和中值相等的数,导致小于中值的数放不进去,需要先统计小于中值的数有多少个,然后用等于中值的数来补全左区间剩余的位置。
因此需要一个变量来标记一下可放中值数的个数。
2、查询
递归查询,判断查询 区间中有多少个进入左区间的数,如果K大于该数,说明所查的数在右区间,否则在左区间。因为该层下一层中左区间的数不全是由所查询区间得出的,还有一部分来自于所查询区间的左侧区间,所以在
进入下一层查询时,应当避开这些无关的数,右区间亦然。
具体细节见代码解释:(以 POJ 2104为例)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<queue> 7 #include<stack> 8 #include<deque> 9 #include<map> 10 #include<iostream> 11 using namespace std; 12 typedef long long LL; 13 const double pi=acos(-1.0); 14 const double e=exp(1); 15 const int N = 100010; 16 17 #define lson i << 1,l,m 18 #define rson i << 1 | 1,m + 1,r 19 20 int order[N],tree[20][N]; 21 int lgo[20][N]; 22 int ans=0,n; 23 24 bool cmp(int a,int b) 25 { 26 return a<b; 27 } 28 29 void build(int ceng,int l,int r) 30 { 31 32 if(l==r) 33 return ; 34 35 int i,p,j; 36 int le,ri,flag=0,mid; //flag存储该进入左区间的中值的个数 37 mid=(l+r) >> 1; 38 39 flag=mid-l+1; //改的地方 左区间所能存的数的个数 40 for(i=l;i<=r;i++) 41 if(tree[ceng][i]<order[mid]) //改的地方 左区间中应存有的中值的个数 42 flag--; 43 44 le=l; 45 ri=mid+1; 46 for(i=l;i<=r;i++) 47 { 48 if(i==l) 49 lgo[ceng][i]=0; 50 else 51 lgo[ceng][i]=lgo[ceng][i-1]; //记录有多少个数进入左区间 52 53 if(tree[ceng][i]<order[mid]||(tree[ceng][i]==order[mid]&&flag>0)) 54 { 55 tree[ceng+1][le++]=tree[ceng][i]; 56 lgo[ceng][i]++; 57 if(tree[ceng][i]==order[mid]) 58 flag--; 59 } 60 else 61 { 62 tree[ceng+1][ri++]=tree[ceng][i]; 63 } 64 } 65 66 build(ceng+1,l,mid); 67 build(ceng+1,mid+1,r); 68 } 69 70 void query(int l,int r,int ql,int qr,int ceng,int k) 71 { 72 73 if(l==r) 74 { 75 ans=tree[ceng][l]; 76 return ; 77 } 78 int lgol,at_left,at_right,ingol; 79 80 int mid=(l+r) >> 1; 81 82 if(ql==l) 83 lgol=0; 84 else 85 lgol=lgo[ceng][ql-1]; //改 查询区间的左侧区间进入左区间的个数 86 ingol=lgo[ceng][qr]-lgol; //改 查询区间进入左区间的个数 87 88 if(k<=ingol) 89 { 90 at_left=l+lgol; //有效的左起始位置 91 query(l,mid,at_left,at_left+ingol-1,ceng+1,k); 92 } 93 else 94 { 95 at_left=mid+1+ql-l-lgol; //有效的左起始位置 96 query(mid+1,r,at_left,at_left+qr-ql+1-ingol-1,ceng+1,k-ingol); 97 } 98 } 99 100 int main() 101 { 102 int i,p,j,k; 103 int a,b,m; 104 scanf("%d%d",&n,&m); 105 for(i=1;i<=n;i++) 106 { 107 scanf("%d",&tree[0][i]); 108 order[i]=tree[0][i]; 109 } 110 sort(order+1,order+1+n,cmp); 111 build(0,1,n); 112 for(j=1;j<=m;j++) 113 { 114 ans=0; 115 scanf("%d%d%d",&a,&b,&k); 116 query(1,n,a,b,0,k); 117 printf("%d ",ans); 118 } 119 120 return 0; 121 122 }