syzoj247
主席树
为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。
线段树有个非常经典的应用是处理RMQ问题,即区间最大/最小值询问问题。现在我们把这个问题可持久化一下:
Q k l r 查询数列在第k个版本时,区间[l, r]上的最大值
M k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本
最开始会给你一个数列,作为第1个版本。每次M操作会导致产生一个新的版本。
修改操作可能会很多呢,如果每次都记录一个新的数列,空间和时间上都是令人无法承受的。
所以我们需要可持久化数据结构:
对于最开始的版本1,我们直接建立一颗线段树,维护区间最大值。
修改操作呢?我们发现,修改只会涉及从线段树树根到目标点上一条树链上logn个节点而已,其余的节点并不会受到影响。所以对于每次修改操作,我们可以只重建修改涉及的节点即可。就像这样:
需要查询第k个版本的最大值,那就从第k个版本的树根开始,像查询普通的线段树一样查询即可。
要计算好所需空间哦
输入格式
第一行两个整数N, Q。N是数列的长度,Q表示询问数
第二行N个整数,是这个数列
之后Q行,每行以0或者1开头,0表示查询操作Q,1表示修改操作M,格式为
0 k l r 查询数列在第k个版本时,区间[l, r]上的最大值 或者
1 k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本
输出格式
对于每个M询问,输出正确答案
样例
##input
4 5
1 2 3 4
0 1 1 4
1 1 3 5
0 2 1 3
0 2 4 4
0 1 2 4
#output
4
5
4
4
#include<cstdio> #include<cctype> #include<algorithm> #define N 10002 #define M 100002 using namespace std; int n,q,root[N],nr,cnt,a,b,c,d; struct data{int l,r,ls,rs,val;}tr[M<<4]; inline void read(int &x){ char ch=getchar();x=0; while(!isdigit(ch))ch=getchar(); while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} } inline void buildtr(int root,int l,int r){ tr[root].l=l;tr[root].r=r; if(l==r){read(tr[root].val);return;} int mid=(l+r)>>1;tr[root].ls=++cnt;tr[root].rs=++cnt; buildtr(tr[root].ls,l,mid);buildtr(tr[root].rs,mid+1,r); tr[root].val=max(tr[tr[root].ls].val,tr[tr[root].rs].val); } inline void change(int old,int root){ tr[root].l=tr[old].l;tr[root].r=tr[old].r; if(tr[old].l==tr[old].r){tr[root].val=d;return;} int mid=(tr[old].l+tr[old].r)>>1; if(c<=mid){ tr[root].ls=++cnt;tr[root].rs=tr[old].rs; change(tr[old].ls,tr[root].ls); } else{ tr[root].ls=tr[old].ls;tr[root].rs=++cnt; change(tr[old].rs,tr[root].rs); } tr[root].val=max(tr[tr[root].ls].val,tr[tr[root].rs].val); } inline int search(int now,int l,int r){ if(tr[now].l>=l&&tr[now].r<=r)return tr[now].val; int res=0,mid=(tr[now].l+tr[now].r)>>1; if(mid>=l)res=search(tr[now].ls,l,r); if(mid<r)res=max(res,search(tr[now].rs,l,r)); return res; } int main(){ read(n);read(q); buildtr(root[++nr],1,n); while(q--){ read(a);read(b);read(c);read(d); if(a){ root[++nr]=++cnt; change(root[b],root[nr]); } else printf("%d\n",search(root[b],c,d)); } }