• 划分树


    划分树,类似线段树,主要用于求解某个区间的第k 大元素(时间复杂度log(n)),快排本也可以快速找出,但快排会改变原序列,所以每求一次都得恢复序列。

    什么是划分树?

      划分树是一种基于线段树的数据结构,也利用了分治的思想,却比线段树高效很多,这是为什么?因为划分树又多了一个性质:在划分时不是随意划分,也不是排序后直接划分(因为这样会破坏原有结构),而是排序后仍保持原来的相对顺序再分到左右子树。

    具体实现方法:

      整个过程分为建树和查询两个阶段:

     1 #include<stdio.h>
     2 #include<iostream>
     3 #include<string.h>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 const int MAXN=100010;
     8 int tree[30][MAXN];//表示每层每个位置的值
     9 int sorted[MAXN];//已经排序的数
    10 int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边
    11 
    12 void build(int l,int r,int dep)
    13 {
    14     if(l==r)return;
    15     int mid=(l+r)>>1;
    16     int same=mid-l+1;//表示等于中间值而且被分入左边的个数
    17     for(int i=l;i<=r;i++)
    18         if(tree[dep][i]<sorted[mid])
    19             same--;
    20     int lpos=l;
    21     int rpos=mid+1;
    22     for(int i=l;i<=r;i++)
    23     {
    24         if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
    25             tree[dep+1][lpos++]=tree[dep][i];
    26         else if(tree[dep][i]==sorted[mid]&&same>0)
    27         {
    28             tree[dep+1][lpos++]=tree[dep][i];
    29             same--;
    30         }
    31         else  //比中间值大分入右边
    32             tree[dep+1][rpos++]=tree[dep][i];
    33         toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数
    34 
    35     }
    36     build(l,mid,dep+1);
    37     build(mid+1,r,dep+1);
    38 
    39 }
    40 
    41 
    42 //查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
    43 int query(int L,int R,int l,int r,int dep,int k)
    44 {
    45     if(l==r)return tree[dep][l];
    46     int mid=(L+R)>>1;
    47     int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左子树的个数
    48     if(cnt>=k)
    49     {
    50         //L+要查询的区间前被放在左边的个数
    51         int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
    52         //左端点加上查询区间会被放在左边的个数
    53         int newr=newl+cnt-1;
    54         return query(L,mid,newl,newr,dep+1,k);
    55     }
    56     else
    57     {
    58         int newr=r+toleft[dep][R]-toleft[dep][r];  // 我们一开始默认左子树是满的,那么右子树的最后一个元素下标就是r,如果[r+1,R]还有属于左子树我们就加进去就好了
    59         int newl=newr-(r-l-cnt);   // newr = newl + (r-l+1-cnt) - 1   , (r-l+1-cnt) 代表[l,r]之间属于右子树的个数
    60         return query(mid+1,R,newl,newr,dep+1,k-cnt);    // 因为是查右子树了,所以 k = k - cnt
    61     }
    62 }
    63 
    64 
    65 int main()
    66 {
    67     //freopen("in.txt","r",stdin);
    68     //freopen("out.txt","w",stdout);
    69     int T;
    70     int n,m;
    71     int s,t,k;
    72     scanf("%d",&T);
    73     while(T--)
    74     {
    75         scanf("%d%d",&n,&m);
    76         memset(tree,0,sizeof(tree));//这个必须
    77         for(int i=1;i<=n;i++)//从1开始
    78         {
    79             scanf("%d",&tree[0][i]);
    80             sorted[i]=tree[0][i];
    81         }
    82         sort(sorted+1,sorted+n+1);
    83         build(1,n,0);
    84         while(m--)
    85         {
    86             scanf("%d%d%d",&s,&t,&k);
    87             printf("%d
    ",query(1,n,s,t,0,k));
    88         }
    89     }
    90     return 0;
    91 }
  • 相关阅读:
    poj 1035 字符串匹配
    拓扑排序的小总结
    POJ1018
    POJ1328详细题解
    POJ1159题解报告
    POJ1088 (滑雪)
    树状树组区间修改,单点修改模板
    spfa模板
    树状树组离散化求逆序对模板
    POJ3723(最小生成树,负权)
  • 原文地址:https://www.cnblogs.com/-Ackerman/p/11386000.html
Copyright © 2020-2023  润新知