概念
主席树,又称可持久化线段树,
其实就是对于每一次修改之后的树的状态的储存
也就是说我们可以对这个树的任意一个状态进行操作
实现
我们考虑修改之后到底修改了多少个点,
也就是修改之后的线段树和修改之前的线段树有哪些点是不同的
通过储存这些修改的点
空间复杂度就可以达到\(O(n+n*log_n)\)
但是时间复杂度依旧是\(O(log_n)\)
打个比方
A因作业没做完要抄B的作业
但A因为时间不够,A想尽可能的少抄
怎么实现呢,如果A发现B的作业的某一道题错了
A就在自己的作业的那一道题上打个标记
其它的题就直接链接到B的作业上去了
但是如果B要改作业的答案呢?
很明显,A会把B摁住不让他修改
因为B修改之后,A的作业就需要重新抄写
也就是说,
老师的查作业方法一定是问A某道题的答案是什么
而并不能看A的作业答案
这里也反映出主席树的一个限制,
一个状态定下来之后就不能修改了
例题
代码
#include<iostream>
using namespace std;
struct node
{
int l,r;
int lson;
int rson;
int maxx;
}tre[32*100000];
int n,q;
int cm;
int cnt;
int k,l,r;
int p,v;
int tot;
int root[1000005];
int build(int l,int r)
{
int now;
tot++;
now=tot;
tre[tot].l=l;
tre[tot].r=r;
if(l==r)
{
cin>>tre[tot].maxx;
return tot;
}
int mid=(l+r)>>1;
tre[now].lson=build(l,mid);
tre[now].rson=build(mid+1,r);
tre[now].maxx=max(tre[tre[now].lson].maxx,tre[tre[now].rson].maxx);
return now;
}
int ask(int k,int l,int r)
{
if(tre[k].r<l||r<tre[k].l)
return 0;
if(l<=tre[k].l&&tre[k].r<=r)
return tre[k].maxx;
return max(ask(tre[k].lson,l,r),ask(tre[k].rson,l,r));
}
int change(int k,int p,int v)
{
int now;
int mid=(tre[k].l+tre[k].r)>>1;
if(tre[k].l==tre[k].r&&tre[k].l==p)
{
tre[++tot].l=tre[k].l;
tre[tot].r=tre[k].r;
tre[tot].maxx=v;
return tot;
}
if(tre[k].l<=p&&p<=mid)
{
tot++;
now=tot;
tre[tot].l=tre[k].l;
tre[tot].r=tre[k].r;
tre[tot].rson=tre[k].rson;
tre[tot].lson=change(tre[k].lson,p,v);
}
else
{
tot++;
now=tot;
tre[tot].l=tre[k].l;
tre[tot].r=tre[k].r;
tre[tot].lson=tre[k].lson;
tre[tot].rson=change(tre[k].rson,p,v);
}
tre[now].maxx=max(tre[tre[now].lson].maxx,tre[tre[now].rson].maxx);
return now;
}
int main()
{
// ios::sync_with_stdio(false);
cin>>n>>q;
root[++cnt]=1;
build(1,n);
for(int i=1;i<=q;i++)
{
cin>>cm;
if(cm==0)
{
cin>>k>>l>>r;
cout<<ask(root[k],l,r)<<'\n';
}
else
{
cin>>k>>p>>v;
root[++cnt]=change(root[k],p,v);
}
}
return 0;
}