可持久化线段树的前置知识是权值线段树,但是你不学也没有太大的关系因为思想不是很难理解。


可持久化线段树支持历史记录查询,这是它赖以解题的方法。

在本题中思路是建立n颗线段树,然后对于每次询问,考虑其中两颗:由于这n颗线段树的结构都一样,而且区间是可以加减的,所以我们将它们相减,再搜一下就可以得到第K大了。


我们每一颗线段树,维护的是元素在区间中出现的次数。

假如直接建n颗树,空间不能接受,因此我们可以考虑简化。可以发现,这些区间都是[1,i]的形式,因此左半部分事实上都是相同的,每一次建立新的树我们只用右半部分新建,重复利用左半部分即可,这样的话空间就可以接受了。


重要变量

int a[N],b[N],T[N];
int sum[N*DEPTH],L[N*DEPTH],R[N*DEPTH];

a数组是题目给出的数组。

b数组是经过排序去重之后的数组。

T数组储存每一颗线段树的根节点(和普通线段树不一样,可持久化线段树的建树和更新都有返回值)。

sum数组就是区间中数的个数。

L和R表示左儿子与右儿子。


建树

inline int build(int l,int r){
    int pos=tot++,mid=(l+r)>>1;
    if(l<r){
        build(l,mid),build(mid+1,r);
    }
    return pos;
}

首先先新建节点,然后递归建树,没什么好讲的。

注意函数末尾返回新建节点的编号(用于T数组的赋值)。


更新

inline int update(int l,int r,int v,int pre){
    int pos=tot++,mid=(l+r)>>1;
    L[pos]=L[pre],R[pos]=R[pre],sum[pos]=sum[pre]+1;
    if(l<r){
        if(v<=mid)L[pos]=update(l,mid,v,L[pre]);
        else R[pos]=update(mid+1,r,v,R[pre]);
    }
    return pos;
}

和普通线段树的更新相比,可持久化线段树的少了一个pos表示当前位置,多了一个pre表示之前的。

首先还是先新建一个节点(毕竟我们可持久化就是一颗颗树),然后新的这一个部分先连向之前那个节点的左右儿子。

这个时候,我们再根据更新的值去重新确定新的左右儿子。

然后最后返回节点编号(为了顶上的确定流程)。


查询

inline int query(int phase1,int phase2,int l,int r,int k){
    int t=sum[L[phase2]]-sum[L[phase1]],mid=(l+r)>>1;
    if(l<r){
        if(k<=t)return query(L[phase1],L[phase2],l,mid,k);
        return query(R[phase1],R[phase2],mid+1,r,k-t);
    }
}

查询操作比较好理解,有点像差分。

就是我们有两个版本的线段树,然后拿他们相减,一路往下找,最后拿到答案即可。


然后就写完了。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
namespace ElderTree{
    const qwq N=200200,DEPTH=23;
    inline qwq read(){
        qwq x=0,f=1;char c=getchar();
        for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
        for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
        return x*f;
    }
    qwq n,m,q,tot;
    qwq a[N],b[N],T[N];
    qwq sum[N*DEPTH],L[N*DEPTH],R[N*DEPTH];
    inline qwq build(qwq l,qwq r){
        qwq pos=tot++,mid=(l+r)>>1;
        if(l<r){
            build(l,mid),build(mid+1,r);
        }
        return pos;
    }
    inline qwq update(qwq l,qwq r,qwq v,qwq pre){
        qwq pos=tot++,mid=(l+r)>>1;
        L[pos]=L[pre],R[pos]=R[pre],sum[pos]=sum[pre]+1;
        if(l<r){
            if(v<=mid)L[pos]=update(l,mid,v,L[pre]);
            else R[pos]=update(mid+1,r,v,R[pre]);
        }
        return pos;
    }
    inline qwq query(qwq phase1,qwq phase2,qwq l,qwq r,qwq k){
        qwq t=sum[L[phase2]]-sum[L[phase1]],mid=(l+r)>>1;
        if(l<r){
            if(k<=t)return query(L[phase1],L[phase2],l,mid,k);
            return query(R[phase1],R[phase2],mid+1,r,k-t);
        }
    }
}
using namespace ElderTree;
qwq main(){
    n=read(),q=read();
    for(qwq i=1;i<=n;++i)a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1),m=unique(b+1,b+n+1)-1-b,T[0]=build(1,m);
    for(qwq i=1;i<=n;++i){
        qwq t=lower_bound(b+1,b+m+1,a[i])-b;
        T[i]=update(1,m,t,T[i-1]);
    }
    while(q--){
        qwq x,y,z;
        x=read(),y=read(),z=read();
        cout<<b[query(T[x-1],T[y],1,m,z)]<<endl;
    }
    return 0;
}

题解 P3834 【【模板】可持久化线段树 1(主席树)】的更多相关文章

  1. 洛谷P3834 [模板]可持久化线段树1(主席树) [主席树]

    题目传送门 可持久化线段树1(主席树) 题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定 ...

  2. P3919 【模板】可持久化数组 -初步探究主席树

    本篇blog主要是给自己(大家)看的. 感谢longlongzhu123奆佬(此人初二LCT)的指点,使本蒟蒻可以快速开始主席树入门. what is 主席树? $        $主席树这个名字只不 ...

  3. 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

    如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...

  4. 【题解】BZOJ3489 A Hard RMQ problem(主席树套主席树)

    [题解]A simple RMQ problem 占坑,免得咕咕咕了,争取在2h内写出代码 upd:由于博主太菜而且硬是要用指针写两个主席树,所以延后2hQAQ upd:由于博主太菜而且太懒所以他决定 ...

  5. POJ 2104 K-th Number(分桶,线段树,主席树)

    一道比较经典的数据结构题.可以用多种方式来做. 一,分桶法(平方分解). 根据数字x的大小和区间内不大于x的数字数量cnt的单调性,可知第k大数kth对应的cnt应该满足cnt≥k, 且kth是满足条 ...

  6. 【BZOJ4771】七彩树(主席树)

    [BZOJ4771]七彩树(主席树) 题面 BZOJ 题解 如果没有深度限制,每次只询问子树内的颜色个数,除了树套树\(dfs\)序加前驱或者后继强行二维数点之外,还有这样一种做法: 把所有相同颜色的 ...

  7. 洛谷P3248 树 [HNOI2016] 主席树+倍增+分治

    正解:主席树+倍增+分治 解题报告: 传送门! 首先看到这题会想到之前考过的这题 但是那题其实简单一些,,,因为那题只要用个分治+预处理就好,只是有点儿思维难度而已 这题就不一样,因为它说了是按照原树 ...

  8. poj 2104 K-th Number 划分树,主席树讲解

    K-th Number Input The first line of the input file contains n --- the size of the array, and m --- t ...

  9. BZOJ_2588_Spoj 10628. Count on a tree_树剖+主席树

    BZOJ_2588_Spoj 10628. Count on a tree_树剖+主席树 题意: 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastan ...

  10. POJ 2761 Feed the dogs(平衡树or划分树or主席树)

    Description Wind loves pretty dogs very much, and she has n pet dogs. So Jiajia has to feed the dogs ...

随机推荐

  1. 51nod 1392 装盒子(费用流)

    如果权值为\(1\)就是最长反链. 然而并不是.考虑用费用流. 把每一个盒子\(i\)拆成i和\(i+n\). 设源点为\(S\),汇点为\(T\). \(S\)向每一个i连容量为\(1\),费用为\ ...

  2. BZOJ 2342 [SHOI2011]双倍回文 (回文自动机)

    题目大意:略 先建出$PAM$ 因为双倍回文串一定是4的倍数,所以找出$PAM$里所有$dep$能整除4的节点 看这个串是否存在一个回文后缀,长度恰好为它的一半,沿着$pre$链往上跳就行了 暴跳可能 ...

  3. 正式版的Linux Kernel 5.1来了,非LTS

    大神Linus Torvalds于今天发布了Linux Kernel 5.1内核正式版,在对现有功能进行改进的同时还带来了很多重要的改进.本次版本更新历时一个半月,不过值得注意的是它并非是长期支持版本 ...

  4. qt 摄像头程序

    http://www.oschina.net/code/snippet_124925_3789?p=3#comments http://www.codesoso.net/Search?q=qt+%C9 ...

  5. poi操作excel2007(读取、生成、编辑)

    因为现在再写excel2003版的比较low,所以我在这就不介绍了,直接介绍2007,我所用的编程软件是IDEA poi操作office总共有6个jar包,在pom.xml文件中配置如下,也可下载后直 ...

  6. [terry笔记]GoldenGate_迁移同步_主库零停机

    ogg根据scn同步数据,源库零停机时间 本次实验与上次的区别:更加注重细节,几乎包含所有步骤,把我越到的坑都作出了说明.并且同步是由10g向11g进行同步,更加符合升级迁移需求. 如下是主要步骤: ...

  7. ASP.NET-datatable转换成list对象

    #region 讲DataTable转换为List对象 /// <summary> /// 利用反射将DataTable转换为List<T>对象 /// </summar ...

  8. Tokyo Tyrant(TTServer)系列(二)-启动參数和配置

    启动參数介绍         ttserver命令能够启动一个数据库实例.由于数据库已经实现了Tokyo Cabinet的抽象API,所以能够在启动的时候指定数据库的配置类型. 支持的数据库类型有: ...

  9. [Angular] Send Data via HTTP using Angular HttpParams

    Obviously in a real world application we do not only fetch data from the backend, but we also send d ...

  10. Linux文件系统(七)---系统调用之open操作(一)

    (内核2.4.37) 一. 当我们打开一个文件的时候.须要获得文件的文件描写叙述符(前面已经说过事实上就是文件数组下标).通常是通过函数open来完毕.这个系统调用在<unistd.h>头 ...