题目描述
为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。
线段树有个非常经典的应用是处理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
解释
序列版本1: 1 2 3 4
查询版本1的[1, 4]最大值为4
修改产生版本2: 1 2 5 4
查询版本2的[1, 3]最大值为5
查询版本1的[4, 4]最大值为4
查询版本1的[2, 4]最大值为4
数据范围与提示
N <= 10000
Q <= 100000
对于每次询问操作的版本号k保证合法,区间[l, r]一定满足1 <= l <= r <= N
最后
他还有一个高贵冷艳的名字:主席树。 然后你A了就可以去装13了
思路{
简单主席树板子题,
相当于一路把原线段树赋给新的线段树即可,只不过需数组标记左右儿子;
}
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<ctime> #include<cmath> #include<map> #include<set> #define MAXX 100010 using namespace std; int tree[MAXX*30],n,Q,a[MAXX],num,ls[MAXX*30],rs[MAXX*30],t[MAXX],all=1; void build(int &x,int l,int r){ x=++num;if(l==r){tree[x]=a[l];return;} int mid=(l+r)>>1;build(ls[x],l,mid),build(rs[x],mid+1,r); tree[x]=max(tree[ls[x]],tree[rs[x]]); } void update(int yy,int &x,int l,int r,int p,int v){ x=++num;ls[x]=ls[yy],rs[x]=rs[yy]; if(l==r){tree[x]=v;return;} int mid=(l+r)>>1; if(p<=mid)update(ls[yy],ls[x],l,mid,p,v); else update(rs[yy],rs[x],mid+1,r,p,v); tree[x]=max(tree[rs[x]],tree[ls[x]]); } int query(int x,int l,int r,int ax,int ay){ if(l>=ax&&r<=ay)return tree[x]; int mid=(l+r)>>1;if(mid<ax)return query(rs[x],mid+1,r,ax,ay); else if(mid>=ay)return query(ls[x],l,mid,ax,ay); else return max(query(ls[x],l,mid,ax,ay),query(rs[x],mid+1,r,ax,ay)); } int main(){ scanf("%d%d",&n,&Q); for(int i=1;i<=n;++i)scanf("%d",&a[i]); build(t[1],1,n); for(int i=1;i<=Q;++i){ int flag,k,p,v;scanf("%d%d%d%d",&flag,&k,&p,&v); if(flag)update(t[k],t[++all],1,n,p,v); else printf("%d ",query(t[k],1,n,p,v)); } return 0; }