syzoj247

主席树

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

线段树有个非常经典的应用是处理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
#include<cstdio>
#include<cctype>
#include<algorithm>
#define N 10002
#define M 100002
using namespace std;
int n,q,root[N],nr,cnt,a,b,c,d;
struct data{int l,r,ls,rs,val;}tr[M<<4];

inline void read(int &x){
    char ch=getchar();x=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
}

inline void buildtr(int root,int l,int r){
    tr[root].l=l;tr[root].r=r;
    if(l==r){read(tr[root].val);return;}
    int mid=(l+r)>>1;tr[root].ls=++cnt;tr[root].rs=++cnt;
    buildtr(tr[root].ls,l,mid);buildtr(tr[root].rs,mid+1,r);
    tr[root].val=max(tr[tr[root].ls].val,tr[tr[root].rs].val);
}

inline void change(int old,int root){
    tr[root].l=tr[old].l;tr[root].r=tr[old].r;
    if(tr[old].l==tr[old].r){tr[root].val=d;return;}
    int mid=(tr[old].l+tr[old].r)>>1;
    if(c<=mid){
        tr[root].ls=++cnt;tr[root].rs=tr[old].rs;
        change(tr[old].ls,tr[root].ls);
    }
    else{
        tr[root].ls=tr[old].ls;tr[root].rs=++cnt;
        change(tr[old].rs,tr[root].rs);
    }
    tr[root].val=max(tr[tr[root].ls].val,tr[tr[root].rs].val);
}

inline int search(int now,int l,int r){
    if(tr[now].l>=l&&tr[now].r<=r)return tr[now].val;
    int res=0,mid=(tr[now].l+tr[now].r)>>1;
    if(mid>=l)res=search(tr[now].ls,l,r);
    if(mid<r)res=max(res,search(tr[now].rs,l,r));
    return res;
}

int main(){
    read(n);read(q);
    buildtr(root[++nr],1,n);
    while(q--){
        read(a);read(b);read(c);read(d);
        if(a){
            root[++nr]=++cnt;
            change(root[b],root[nr]);
        }
        else printf("%d\n",search(root[b],c,d));
    }
}

 

posted @ 2018-04-20 10:57  lnyzo  阅读(103)  评论(0编辑  收藏  举报