可持久化线段树,可持久化权值线段树(主席树)

前言:

今天听了钟长者的课,听了分块,莫队,可持久化线段树,主席树.....因为明天没有模拟赛,所以整理一下。

等等,为什么可持久化线段树和主席树分开了,我一直以为它们是一个数据结构,但钟长者说并不是。

钟长者言:“可持久化线段树是线段树的一个托展,主席树是可持久化线段树的一个具体应用。”

那就跟随钟长者的脚步从可持久化线段树开始,


一,可持久化线段树

所谓可持久化数据结构,就是要记录在某次操作之后的状态,比如对于一个普通的线段树,我们有m次

操作,但对于第i次操作之后(1<=i<=m),我们就不知道i之前线段树的样子了,可持久化线段树就是

让我们记住它之前的样子。

1,一种暴力的做法

那么,对于上述的问题情境,有一种很简单的做法啊,就是对于每次操作i后我们都建一棵线段树,这样

如果我们询问某次操作之后的数据,直接查询对应的线段树即可。

2,对于暴力做法的优化

在上述做法中,我们不难发现,无论是时间还是空间都不是很优,那么我们怎么优化呢,在此我们只考虑存在

单点修改的情况,不考虑有区间修改,(因为博主菜QwQ),我们会发现对于每个单点修改,很像树状数组的一点,

只会影响到含有那个点区间的线段树上的节点,那么我们不难想到,我们对于第i次修改操作,只需要记一下它所

影响的那条链即可,然后我们将这条链上的点连回原树,就完成了可持久化线段树。

3,可持久化线段树的基本操作

a.建树

因为要重新记录一下受影响的链,所以我们不能像线段树那样对于每个节点编号,我们在建树的时候要记一下

每个点的左二子和右儿子的编号。

 1 struct node
 2 {
 3     int lc,rc;
 4     int sum;
 5     node()
 6     {
 7         lc=rc=sum=0;
 8     }
 9 };
10 node tree[maxn*5];
11 inline int build(int l,int r)
12 {
13     int k=++cnt;
14     if(l==r)
15     {
16         tree[k].sum=num[l];
17         return k;
18     }
19     int mid=(l+r)>>1;
20     tree[k].lc=build(l,mid); tree[k].rc=build(mid+1,r);
21     tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
22     return k;
23 }

b.修改操作

与普通的线段树不同,可持久化线段树要记下第i次操作后情况,所以对于第i次操作影响的节点,我们都

建一个新的点,然后将不变的信息连回这条链上即可。

 1 inline int modify(int rt,int l,int r,int p,int v)
 2 {
 3     int new_rt=++cnt;
 4     tree[cnt]=tree[rt];//先把原信息复制上,再修改即可实现
 5     if(l==r)
 6     {
 7         tree[new_rt].sum+=v;
 8         return new_rt;
 9     }
10     int mid=(l+r)>>1;
11     if(p<=mid) tree[new_rt].lc=modify(tree[rt].lc,l,mid,p,v);
12     else tree[new_rt].rc=modify(tree[st].rc,mid+1,r,p,v); 
13     tree[new_rt].sum=tree[tree[new_rt].lc].sum+tree[tree[new_rt].rc].sum;
14     return new_rt;
15 }

c.查询操作

同线段树的基础操作一样,只需要从要查询到的时间对应的树根开始查询即可。


二,可持久化权值线段树——主席树

猜你喜欢

转载自www.cnblogs.com/Hoyoak/p/11628883.html