可持久化线段树

题目描述

为什么说本题是福利呢?因为这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。

线段树有个非常经典的应用是处理RMQ问题,即区间最大/最小值询问问题。现在我们把这个问题可持久化一下:

Q k l r 查询数列在第k个版本时,区间[l, r]上的最大值

M k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本

最开始会给你一个数列,作为第1个版本。每次M操作会导致产生一个新的版本。

修改操作可能会很多呢,如果每次都记录一个新的数列,空间和时间上都是令人无法承受的。

所以我们需要可持久化数据结构:

Markdown

对于最开始的版本1,我们直接建立一颗线段树,维护区间最大值。

修改操作呢?我们发现,修改只会涉及从线段树树根到目标点上一条树链上logn个节点而已,其余的节点并不会受到影响。所以对于每次修改操作,我们可以只重建修改涉及的节点即可。就像这样:

Markdown

需要查询第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;
}

原文地址:https://www.cnblogs.com/zzmmm/p/6627198.html