划分树简单介绍

我感觉划分树的基本思想是二分和归并排序,分为建树和查询两个部分。

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\n",ans);
118     }
119 
120     return 0;
121 
122 }

 

posted @ 2018-08-09 18:49  Daybreaking  阅读(274)  评论(0编辑  收藏  举报