• 【模板】可持久化线段树(主席树)


    Description

    这是一道非常直白的可持久化线段树的练习题,目的并不是虐人,而是指导你入门可持久化数据结构。
    线段树有个非常经典的应用是处理RMQ问题,即区间最大/最小值询问问题。现在我们把这个问题可持久化一下:
    Q k l r 查询数列在第k个版本时,区间[l, r]上的最大值
    M k p v 把数列在第k个版本时的第p个数修改为v,并产生一个新的数列版本
    最开始会给你一个数列,作为第1个版本。
    每次M操作会导致产生一个新的版本。修改操作可能会很多呢,如果每次都记录一个新的数列,空间和时间上都是令人无法承受的。所以我们需要可持久化数据结构:

    对于最开始的版本1,我们直接建立一颗线段树,维护区间最大值。
    修改操作呢?我们发现,修改只会涉及从线段树树根到目标点上一条树链上logn个节点而已,其余的节点并不会受到影响。所以对于每次修改操作,我们可以只重建修改涉及的节点即可。就像这样: P

    需要查询第k个版本的最大值,那就从第k个版本的树根开始,像查询普通的线段树一样查询即可。

    Input

    第一行两个整数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,并产生一个新的数列版本

    Output

    对于每个M询问,输出正确答案

    Sample 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

    Sample Output

    4
    5
    4
    4

    Hint

    样例解释:
    序列版本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

    如题目中算法详解,

    我的实现:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 const int N=10005,M=100005,INF=-2e8;
     8 struct node{
     9     int ls,rs,val;
    10 }t[M*10];
    11 int gi()
    12     {
    13         int str=0,f=1;char ch=getchar();
    14         while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    15         while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
    16         return str*f;
    17     }
    18 int a[N],root[M],tot=0,num=1;
    19 void build(int &rt,int l,int r)
    20     {
    21         rt=++tot;
    22         if(l==r){
    23             t[rt].val=a[l];
    24             return ;
    25         }
    26         int mid=(l+r)>>1;
    27         build(t[rt].ls,l,mid);build(t[rt].rs,mid+1,r);
    28         t[rt].val=max(t[t[rt].ls].val,t[t[rt].rs].val);
    29     }
    30 int query(int rt,int l,int r,int sa,int se)
    31     {
    32         if(l>se || r<sa)return INF;
    33         if(sa<=l && r<=se)return t[rt].val;
    34         int mid=(l+r)>>1;
    35         return max(query(t[rt].ls,l,mid,sa,se),query(t[rt].rs,mid+1,r,sa,se));
    36     }
    37 void change(int &rt,int fa,int l,int r,int p,int to)
    38     {
    39         rt=++tot;
    40         if(l==r){
    41             t[rt].val=to;
    42             return ;
    43         }
    44         int mid=(l+r)>>1;
    45         if(p<=mid)
    46             {
    47                 change(t[rt].ls,t[fa].ls,l,mid,p,to);
    48                 t[rt].rs=t[fa].rs;
    49             }
    50         else
    51             {
    52                 change(t[rt].rs,t[fa].rs,mid+1,r,p,to);
    53                 t[rt].ls=t[fa].ls;
    54             }
    55         t[rt].val=max(t[t[rt].ls].val,t[t[rt].rs].val);
    56     }
    57 int main()
    58     {
    59         int n=gi(),m=gi();
    60         for(int i=1;i<=n;i++)a[i]=gi();
    61         build(root[1],1,n);
    62         int flag,k,l,r;
    63         for(int i=1;i<=m;i++)
    64             {
    65                 flag=gi();k=gi();l=gi();r=gi();
    66                 if(!flag)printf("%d
    ",query(root[k],1,n,l,r));
    67                 else change(root[++num],root[k],1,n,l,r);
    68             }
    69         return 0;
    70     }
  • 相关阅读:
    linux的一些记录
    ant的一些记录
    cmd
    关于请求转发、重定向、url重写
    函数声明优先级高于变量赋值
    认识hasLayout——IE浏览器css bug的一大罪恶根源
    undefined与null的区别
    Javascript面向对象编程(三):非构造函数的继承
    Javascript面向对象编程(二):构造函数的继承
    Javascript 面向对象编程(一):封装
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7107439.html
Copyright © 2020-2023  润新知