2554. [福利]可持久化线段树
★★☆ 输入文件:longterm_segtree.in
输出文件:longterm_segtree.out
简单对比
时间限制:3 s 内存限制:256 MB
【题目描述】
为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。
线段树有个非常经典的应用是处理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询问,输出正确答案
【样例输入】
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
【样例输出】
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
【来源】
lj出题人: sxysxy。原题见: http://syzoj.com/problem/247
思路:
这个,裸模板呀;
来,上代码:
#include <cstdio> #include <iostream> #define maxn 40005 using namespace std; struct TreeNodeType { int lc,rc,dis; }; struct TreeNodeType tree[maxn*30]; int if_z,n,q,root[maxn*10],cnt,tot,li,ri; char Cget; inline void in(int &now) { now=0,if_z=1,Cget=getchar(); while(Cget>'9'||Cget<'0') { if(Cget=='-') if_z=-1; Cget=getchar(); } while(Cget>='0'&&Cget<='9') { now=now*10+Cget-'0'; Cget=getchar(); } now*=if_z; return ; } void tree_build(int &now,int l,int r) { now=++tot; if(l==r) { in(tree[now].dis); return ; } int mid=(l+r)>>1; tree_build(tree[now].lc,l,mid); tree_build(tree[now].rc,mid+1,r); if(tree[tree[now].lc].dis>tree[tree[now].rc].dis) tree[now].dis=tree[tree[now].lc].dis; else tree[now].dis=tree[tree[now].rc].dis; } void tree_add(int pre,int &now,int l,int r) { now=++tot; if(l==r) { tree[now].dis=ri; return ; } int mid=(l+r)>>1; if(li<=mid) { tree_add(tree[pre].lc,tree[now].lc,l,mid); tree[now].rc=tree[pre].rc; } else { tree_add(tree[pre].rc,tree[now].rc,mid+1,r); tree[now].lc=tree[pre].lc; } if(tree[tree[now].lc].dis>tree[tree[now].rc].dis) tree[now].dis=tree[tree[now].lc].dis; else tree[now].dis=tree[tree[now].rc].dis; } int tree_query(int now,int l,int r) { if(l>=li&&r<=ri) return tree[now].dis; int mid=(l+r)>>1; if(li>mid) return tree_query(tree[now].rc,mid+1,r); else if(ri<=mid) return tree_query(tree[now].lc,l,mid); else return max(tree_query(tree[now].lc,l,mid),tree_query(tree[now].rc,mid+1,r)); } int main() { freopen("longterm_segtree.in","r",stdin); freopen("longterm_segtree.out","w",stdout); in(n),in(q); tree_build(root[++cnt],1,n); int type,k; while(q--) { in(type); if(type) { in(k),in(li),in(ri); tree_add(root[k],root[++cnt],1,n); } else { in(k),in(li),in(ri); printf("%d\n",tree_query(root[k],1,n)); } } return 0; }