zoukankan      html  css  js  c++  java
  • 可持久化线段树

    题目描述

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

    线段树有个非常经典的应用是处理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;
    }

  • 相关阅读:
    树形结构菜单,递归实现
    基于Vue的日历组件,可以标注重要日子
    关于element-ui级联菜单(城市三级联动菜单)和回显问题
    继承(面试问到)
    vue监听浏览器刷新
    Popover 弹出框,里面的表格点击后关闭弹窗
    el-table表格合并单元格
    对角线
    ElementUI中el-radio再次点击取消选中
    保留文本框换行和空格
  • 原文地址:https://www.cnblogs.com/zzmmm/p/6627198.html
Copyright © 2011-2022 走看看