• 可持久化线段树(主席树)


    关于可持久化线段树

    可持久化线段树可以将历史版本的线段树记忆下来,并支持新建版本与查询历史版本。

    其中有一道经典的静态主席树的题就是求区间k大。

    建树的过程其实就是先建一棵空树,再按输入顺序插入节点。需要新开节点的时候就新开,如果和之前没有什么变化的话就连到之前的树上。这样的空间复杂度据说是Θ(nlogn)

    由于线段树支持区间减法,于是我们可以在查询区间的时候利用前缀和的思想,对于[L,R]的区间,我们可以让第L-1个线段树与第R个线段树相减。

    代码如下:(附有较详细的注释。)

     1 //Writer : Hsz %WJMZBMR%tourist%hzwer
     2 #include <iostream>
     3 #include <cstring>
     4 #include <cstdio>
     5 #include <cmath>
     6 #include <queue>
     7 #include <map>
     8 #include <set>
     9 #include <stack>
    10 #include <vector>
    11 #include <cstdlib>
    12 #include <algorithm>
    13 #define LL long long
    14 using namespace std;
    15 const int N=200005<<5;
    16 int l[N],r[N],sum[N],b[N];//sum表示线段树区间内点的数量。
    17 int n,m,a[N],root[N],tot;
    18 int build(int L,int R) {
    19     int rt=++tot;
    20     if(L<R) {
    21         l[rt]=build(L,(L+R)>>1);//建一个空树。l[rt]表示rt的左儿子。
    22         r[rt]=build((L+R)/2+1,R);
    23     }
    24     return rt;
    25 }
    26 int update(int pre,int L,int R,int c) {//加点,把历史版本记录下来。
    27     int rt=++tot;
    28     l[rt]=l[pre],r[rt]=r[pre],sum[rt]=sum[pre]+1;//pre:上一棵树的根,因为插入了一个新的点,所以sum肯定比上一个多1。
    29     if(L<R) {
    30         if(c<=((L+R)>>1))l[rt]=update(l[pre],L,(L+R)>>1,c);//如果插入的点在这个线段树的左子树,就递归到左子树。
    31         else r[rt]=update(r[pre],(L+R)/2+1,R,c);//同理。
    32     }
    33     return rt;//返回新建子树的根节点。
    34 }
    35 int query(int u,int v,int L,int R,int k) {
    36     if(L==R) return L;
    37     int x=sum[l[v]]-sum[l[u]];//u:区间左端点的线段树的根,v:右端点的线段树的根。
    38     //如果左子树的增加的点数仍旧大于要求的k,那么就递归到左子树求。
    39     if(x>=k) return query(l[u],l[v],L,(L+R)>>1,k);
    40     //否则递归到右子树,减去左子树上的点个数。
    41     else return query(r[u],r[v],(L+R)/2+1,R,k-x);
    42 }
    43 int main() {
    44     cin>>n>>m;
    45     for(int i=1; i<=n; i++) scanf("%d",&a[i]),b[i]=a[i];
    46     sort(a+1,a+1+n);
    47     int u=unique(a+1,a+1+n)-a-1;
    48     root[0]=build(1,u);
    49     for(int i=1; i<=n; i++) {
    50         b[i]=lower_bound(a+1,a+1+u,b[i])-a;//离散化。
    51         root[i]=update(root[i-1],1,u,b[i]);
    52     }
    53     int q,v,k;
    54     for(int i=1; i<=m; i++) {
    55         scanf("%d%d%d",&q,&v,&k);
    56         printf("%d
    ",a[query(root[q-1],root[v],1,u,k)]);
    57     }
    58     return 0;
    59 }
    我是咸鱼。转载博客请征得博主同意Orz
  • 相关阅读:
    Mybatis学习总结(五)——动态sql
    1006 换个格式输出整数(15分)
    1005 继续(3n+1)猜想(25分) *
    1004 成绩排名(20分)
    1003 我要通过!(20分)*
    1002 写出这个数(20分) *
    1001 害死人不偿命的(3n+1)猜想(15分)
    CCSP 201312-2 ISBN号码
    CCSP201312-1出现次数最多的数
    c++动态定义数组
  • 原文地址:https://www.cnblogs.com/sdfzhsz/p/9027010.html
Copyright © 2020-2023  润新知