主席树的不同建树方式

主席树,又叫可持久化线段树,一种可持久化的数据结构。
一种基本用处是查询区间中排名为k的数字;还有一种是普通的线段树操作区间修改,区间查询,或者区间历史查询;另外还有一种用处是求区间有多少个不同的数字
它们的建树方式有所不同,

第一种的建树方式是先把所有数字去重+离散化,然后得到不重复数字的个数n,然后[l,r]这个节点里存的是当前前缀中[a[l] , a[r]]有多少个数字,这里跟普通的线段树存的东西不太一样,有点类似于权值线段树,不过是离散之后的权值线段树。

第二种的建树方式就是和线段树一样,然后每次修改新增一个 根,把需要修改的节点新建出来(利用lazy可以使得每次新建的节点不超过log n 个)。然后就可以回到过去查询历史的区间信息。有个地方要注意一下,这里的lazy实际上没有pushdown操作,而是标记了区间被加了多少,然后在查询的时候,把这个区间的值层层累加传递下去,直到目标区间,这样减少了pushdown中新增的lazy节点,节省了大量的空间。

第三种的建树方式和第二种类似,如果可以离线的话,我们知道可以让r排序,然后从小到大枚举r。假设当前枚举到r,a[r]前一次出现的位置记为p,p位置减去1,r位置加上1,然后查询[L,R]的和就是这一段有多少个不同数字出现。但是如果是在线的话,当我们询问到r的时候,会把前面某一个点减去,假设下次询问r-x时,就有可能这个点已经被减去导致结果变小。 所以一棵线段树是不够的,我们考虑建n棵线段树,但是每次插入一个 r ,实际上只会新增两条链,(如果该数字没有出现过则是一条)。
然后每次查询[l,r],就是以第r棵线段树的根的[L,R]这段的和。
贴一下几种用法的模板题
第一种用法的模板题,hdu2665

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, n , 1
const int MAXN = 1e5 ;
struct Tree{
    int l, r ;
    int val ;
}tree[MAXN *30+ 30];
int N , n ;
int a[100100] ;
int Root[100100] ;
int tot = 0;
int num[100100] ;
void Push_up(int rt )
{
    tree[rt].val = tree[tree[rt].l].val + tree[tree[rt].r].val ;
}
int Build(int l, int r)
{
    int rt = tot ++ ;
    if(l == r)
    {
        tree[rt].val = 0 ;
        return rt ;
    }
    int mid = (l + r) / 2;
    tree[rt].l = Build(l , mid ) ;
    tree[rt].r = Build(mid + 1<
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值