zoukankan      html  css  js  c++  java
  • 可持久化线段树(主席树)

    关于可持久化线段树

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

    其中有一道经典的静态主席树的题就是求区间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
  • 相关阅读:
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    centos 编码问题 编码转换 cd到对应目录 执行 中文解压
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    Android MVP 十分钟入门!
    mysql备份及恢复
    mysql备份及恢复
    mysql备份及恢复
  • 原文地址:https://www.cnblogs.com/sdfzhsz/p/9027010.html
Copyright © 2011-2022 走看看