【重码数据结构】主席树(可持久化线段树)

例题:https://www.luogu.org/problemnew/show/P3834

主席树用于查询每个历史版本。

这个题代码如下

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200001;
int n,m,tot;
ll a[maxn],b[maxn];
struct stree{
    int lc,rc;
    ll sum;
    #define lc(x) tree[x].lc
    #define rc(x) tree[x].rc
    #define s(x) tree[x].sum
}tree[maxn<<5];
int root[maxn];
inline int build(int l,int r){
    int p=++tot;
    if(l==r) return p;
    int mid=(l+r)>>1;
    lc(p)=build(l,mid);
    rc(p)=build(mid+1,r);
    return p;
}
inline int insert(int now,int l,int r,int x){
    int p=++tot;
    tree[p]=tree[now];
    s(p)++;
    if(l==r)return p;
    int mid=(l+r)>>1;
    if(x<=mid) lc(p)=insert(lc(now),l,mid,x);
    else rc(p)=insert(rc(now),mid+1,r,x);
    return p;
}
inline int ask(int p,int q,int l,int r,int k){//在p,q两个时间节点上,在[l,r]的范围内的数的个数
    if(l==r) return l;
    int mid=(l+r)>>1;
    int lcnt=s(lc(q))-s(lc(p));
    if(k<=lcnt) return ask(lc(p),lc(q),l,mid,k);//在两个节点的左儿子中查找。
    else return ask(rc(p),rc(q),mid+1,r,k-lcnt);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int cnt=unique(b+1,b+n+1)-b-1;
    root[0]=build(1,cnt);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
        root[i]=insert(root[i-1],1,cnt,a[i]);
    }
    while(m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%lld\n",b[ask(root[l-1],root[r],1,cnt,k)]);
    }
    system("pause");
    return 0;
}

例题2:https://www.luogu.org/problemnew/show/P3919

主席树裸题,直接就是模板。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,m,tot;
int root[maxn],a[maxn];
struct stree{
    int lc,rc,val;
    #define lc(x) tree[x].lc
    #define rc(x) tree[x].rc
    #define val(x) tree[x].val
}tree[maxn<<5];
inline int build(int l,int r){
    int p=++tot;
    if(l==r){val(p)=a[l];return p;}
    int mid=(l+r)>>1;
    lc(p)=build(l,mid);
    rc(p)=build(mid+1,r);
    return p;
}
inline int insert(int now,int l,int r,int x,int dat){
    int p=++tot;
    tree[p]=tree[now];
    if(l==r){val(p)=dat;return p;}
    int mid=(l+r)>>1;
    if(x<=mid) lc(p)=insert(lc(now),l,mid,x,dat);
    else rc(p)=insert(rc(now),mid+1,r,x,dat);
    return p;
}
inline int ask(int p,int l,int r,int x){
    if(l==r) return val(p);
    int mid=(l+r)>>1;
    if(x<=mid) return ask(lc(p),l,mid,x);
    else return ask(rc(p),mid+1,r,x);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    root[0]=build(1,n);
    for(int i=1;i<=m;i++){
        int v,op,x,c;
        scanf("%d%d%d",&v,&op,&x);
        if(op==1) scanf("%d",&c),root[i]=insert(root[v],1,n,x,c);
        else printf("%d\n",ask(root[v],1,n,x)),root[i]=root[v];
    }
    // system("pause");
    return 0;
}

原文地址:https://www.cnblogs.com/ChrisKKK/p/11025298.html

时间: 2024-10-05 22:55:42

【重码数据结构】主席树(可持久化线段树)的相关文章

主席树/函数式线段树/可持久化线段树

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

主席树 | | 可持久化线段树

可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 所以这里讲的可持久化线段树也叫函数式线段树(又叫主席树……因为先驱就是fotile主席Orz……). 先了解一下主席树 http://seter.is-programmer.com/posts/31907.html    很详细的介绍了函数式线段树(主席树). 主席树其实就是很多棵线段树,由于每次更新只需要更新logN个节点,所

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

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

[TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]

按Dfs序逐个插入点,建立可持久化线段树,每次查询即可,具体详见代码. 不知道为什么,代码慢的要死,, #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using

BZOJ 2527 Poi2011 Meteors 整体二分+线段树 / 可持久化线段树(MLE)

题目大意:给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值 首先我们考虑暴力想法 对于每个国家分开讨论 二分操作次数 但是这样每次Judge的时候我们要模拟1~mid所有的操作 浪费在这里的复杂度实在太大 这样做每个国家需要模拟O(klogk)次操作 时间复杂度O(nklogk) TLE 我们需要对浪费在这里的复杂度做一些改进 1.可持久化线段树(MLE) 每次二分一个mid之后 我们要找到mid次

BZOJ 3439 Kpm的MC密码 Trie树+可持久化线段树

题目大意:给定n个字符串,对于每个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那么对于每个字符串以它为前缀的字符串一定是连续的 那么就转化成了区间第k小 这个用可持久化线段树可以解决 排序自然不能直接排 既然是字符串 考虑Trie树+DFS即可 注意字符串有重复的 小心 #include <vector> #include <cstdio> #include <

spoj3267 D-query 主席树(可持久化线段树)

题目链接 题意:给n个数,m次查询,求[l,r]之间不重复数的个数. 思路:主席树.用一个map记录每个值在当前操作下最新的位置,从前往后插入主席树.对于查询[l,r],窝们在root[ l ]下查询在r之前的不重复数的个数.详见代码: /********************************************************* file name: spoj3267.cpp author : kereo create time: 2015年04月04日 星期六 14时2

[POJ2104/HDU2665]Kth Number-主席树-可持久化线段树

Problem Kth Number Solution 裸的主席树,模板题.但是求k大的时候需要非常注意,很多容易写错的地方.卡了好久.写到最后还给我来个卡空间. 具体做法参见主席树论文<可持久化数据结构研究>. AC Code #include "cstdio" #include "iostream" #include "cstring" #include "algorithm" using namespace

主席树(可持久化线段树) 静态第k大

可持久化数据结构介绍 可持久化数据结构是保存数据结构修改的每一个历史版本,新版本与旧版本相比,修改了某个区域,但是大多数的区域是没有改变的, 所以可以将新版本相对于旧版本未修改的区域指向旧版本的该区域,这样就节省了大量的空间,使得可持久化数据结构的实现成为了可能. 如下图,就是可持久化链表 插入前 插入后 尽可能利用历史版本和当前版本的相同区域来减少空间的开销. 而主席树(可持久化线段树)的原理同样是这样. 有n个数字,  我们将其离散化,那么总有[1,n]个值,如果建一棵线段树,每个结点维护子