zoukankan      html  css  js  c++  java
  • [福利]可持久化线段树

    ★★☆   输入文件: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

    思路:可持久化线段树

    思路:裸的主席树
    
    你需要一个线段树,支持查询操作;
    
    你还需要一个链表,储存每一个状态;
    
    每次查询时,更新路径上的有用值就行了。
    
    不要在意线段树上有什么,你所有的状态都存在链表里。
    
    代码实现:
    
     1 #include<cstdio>
     2 const int maxn=1e4+10;
     3 const int maxq=1e5+10;
     4 int n,q,cs,id=1;
     5 int a,b,c,d;
     6 struct tree{int s,n;}tr[maxn<<2];
     7 int tm[maxq];
     8 struct change{int s,ln,rn;}cg[maxq<<5];
     9 inline int min_(int x,int y){return x<y?x:y;}
    10 inline int max_(int x,int y){return x>y?x:y;}
    11 void make_t(int t,int l,int r,int k){
    12     if(l==r){
    13         scanf("%d",&tr[k].s);
    14         cg[++cs].s=tr[k].s,tr[k].n=cs;
    15         return;
    16     }
    17     int mid=l+r>>1,ls=k<<1,rs=ls+1;
    18     make_t(t,l,mid,ls);
    19     make_t(t,mid+1,r,rs);
    20     tr[k].s=max_(tr[ls].s,tr[rs].s);
    21     cg[++cs]=(change){tr[k].s,tr[ls].n,tr[rs].n},tr[k].n=cs;
    22 }
    23 void change_t(int t,int l,int r,int k,int p,int f){
    24     if(l==r){
    25         tr[k].s=d;
    26         cg[++cs].s=tr[k].s,tr[k].n=cs;
    27         return;
    28     }
    29     int mid=l+r>>1,ls=k<<1,rs=ls+1;
    30     if(cg[f].ln) tr[ls].s=cg[cg[f].ln].s,tr[ls].n=cg[f].ln; 
    31     if(cg[f].rn) tr[rs].s=cg[cg[f].rn].s,tr[rs].n=cg[f].rn;
    32     if(p<=mid) change_t(t,l,mid,ls,p,cg[f].ln);
    33     else change_t(t,mid+1,r,rs,p,cg[f].rn);
    34     tr[k].s=max_(tr[ls].s,tr[rs].s);
    35     cg[++cs]=(change){tr[k].s,tr[ls].n,tr[rs].n},tr[k].n=cs;
    36 }
    37 int sum_t(int l,int r,int k,int al,int ar,int f){
    38     if(l==al&&r==ar) return tr[k].s;
    39     int mid=l+r>>1,ls=k<<1,rs=ls+1,ans=0;
    40     if(cg[f].ln) tr[ls].s=cg[cg[f].ln].s;
    41     if(cg[f].rn) tr[rs].s=cg[cg[f].rn].s;
    42     if(al<=mid) ans=max_(ans,sum_t(l,mid,ls,al,min_(ar,mid),cg[f].ln));
    43     if(ar>mid) ans=max_(ans,sum_t(mid+1,r,rs,max_(al,mid+1),ar,cg[f].rn));
    44     return ans;
    45 }
    46 int main(){
    47     freopen("longterm_segtree.in","r",stdin);
    48     freopen("longterm_segtree.out","w",stdout);
    49     scanf("%d%d",&n,&q);
    50     make_t(1,1,n,1);
    51     tm[1]=tr[1].n;
    52     while(q--){
    53         scanf("%d%d%d%d",&a,&b,&c,&d);
    54         if(a){
    55             change_t(++id,1,n,1,c,tm[b]);
    56             tm[id]=tr[1].n;
    57         }
    58         else printf("%d
    ",sum_t(1,n,1,c,d,tm[b]));
    59     }
    60     return 0;
    61 }
    R了两遍,一直在找链表的锅,最后发现线段树开小了。。。我内心有点复杂。
    经大佬提醒,发现自己的主席树是错的。

    代码实现:

     1 #include<cstdio>
     2 const int maxn=1e4+10;
     3 const int maxq=1e5+10;
     4 int n,q,ts,ks;
     5 int a,b,c,d;
     6 int tt[maxq];
     7 struct tree{int s,l,r,mid,lp,rp;}t[maxq<<4];
     8 inline int min_(int x,int y){return x<y?x:y;}
     9 inline int max_(int x,int y){return x>y?x:y;}
    10 void build(int l,int r,int k){
    11     t[k].l=l,t[k].r=r;
    12     if(l==r){scanf("%d",&t[k].s);return;}
    13     t[k].mid=l+r>>1,t[k].lp=++ts,t[k].rp=++ts;
    14     build(l,t[k].mid,t[k].lp);
    15     build(t[k].mid+1,r,t[k].rp);
    16     t[k].s=max_(t[t[k].lp].s,t[t[k].rp].s);
    17 }
    18 void change(int k,int nk){
    19     t[nk].l=t[k].l,t[nk].r=t[k].r;
    20     if(t[k].l==t[k].r){t[nk].s=d;return;}
    21     t[nk].mid=t[k].mid;
    22     if(c<=t[k].mid){
    23         t[nk].lp=++ts,t[nk].rp=t[k].rp;
    24         change(t[k].lp,t[nk].lp);
    25     }
    26     else{
    27         t[nk].rp=++ts,t[nk].lp=t[k].lp;
    28         change(t[k].rp,t[nk].rp);
    29     }
    30     t[nk].s=max_(t[t[nk].lp].s,t[t[nk].rp].s);
    31 }
    32 int search(int k,int l,int r){
    33     if(t[k].l==l&&t[k].r==r) return t[k].s;
    34     int ans=0;
    35     if(l<=t[k].mid) ans=search(t[k].lp,l,min_(r,t[k].mid));
    36     if(r>t[k].mid) ans=max_(ans,search(t[k].rp,max_(l,t[k].mid+1),r));
    37     return ans;
    38 }
    39 int main(){
    40     freopen("longterm_segtree.in","r",stdin);
    41     freopen("longterm_segtree.out","w",stdout);
    42     scanf("%d%d",&n,&q);
    43     build(1,n,tt[++ks]);
    44     while(q--){
    45         scanf("%d%d%d%d",&a,&b,&c,&d);
    46         if(a){
    47             tt[++ks]=++ts;
    48             change(tt[b],tt[ks]);
    49         }
    50         else printf("%d
    ",search(tt[b],c,d));
    51     }
    52     return 0;
    53 }

    又因为tt[](链首)数组开小了R了一半。

    题目来源:COGS

  • 相关阅读:
    1436 孪生素数 2
    1702 素数判定 2
    第五章 Spring3.0 、Hibernate3.3与Struts2的整合 基于Annotation
    逻辑服务器和数据缓存服务器交互方式整理
    Solr学习(2) Solr4.2.0+IK Analyzer 2012
    hdu4288 Coder
    解决Robotium测试用例crash问题
    FineUI_动态绑定Grid
    Protection 5 ---- Priviliege Level Checking 2
    用户权限管理
  • 原文地址:https://www.cnblogs.com/J-william/p/6628484.html
Copyright © 2011-2022 走看看