前言:
今天听了钟长者的课,听了分块,莫队,可持久化线段树,主席树.....因为明天没有模拟赛,所以整理一下。
等等,为什么可持久化线段树和主席树分开了,我一直以为它们是一个数据结构,但钟长者说并不是。
钟长者言:“可持久化线段树是线段树的一个托展,主席树是可持久化线段树的一个具体应用。”
那就跟随钟长者的脚步从可持久化线段树开始,
一,可持久化线段树
所谓可持久化数据结构,就是要记录在某次操作之后的状态,比如对于一个普通的线段树,我们有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.查询操作
同线段树的基础操作一样,只需要从要查询到的时间对应的树根开始查询即可。