• 关于fhq treap的一些思考


    发现自己以前就没有真正理解透彻过fhq treap和大部分平衡树

    最近在被这题[NOI2005]维护数列榨干的过程中,发现了一些理解性的漏洞,在这里写出来,也供大家参考和改正。

    不过我可能还没有考虑周全,如有遗漏和错误,欢迎指出.

    一开始期盼地交了一发:10分...{>~<}

    在对拍的时候,我被这样的一组简单的数据(经过整理,原数据太脑残orz)hack了:

    9 5
    -8 10 -5 -6 -7 2 8 -7 -1
    REVERSE 8 1
    REVERSE 3 3
    MAX-SUM
    REVERSE 3 6
    MAX-SUM

    然鹅并不知道为什么错...

    弱弱地查阅了许多资料,询问了很多疑惑,又自己手画思考发现可能错在以下几点:

    1.

    (Merge)的时候,为下传标记,我在翻转区间时的习惯写法是这样:

    int Merge(int x,int y){
        if(!x||!y) return x+y;
        return a[x].d<a[y].d?down(x),a[x].r=Merge(a[x].r,y),Upd(x),x
                           :(down(y),a[y].l=Merge(x,a[y].l),Upd(y),y);
        //down即下传标记
    }
    

    这在仅要求区间翻转的题目中是完全可以的emm...

    但这题布星

    我们考虑这样的情况:

    对,就是在有一课树为时,会出问题。

    这题要求我们动态维护一个最大区间子段和,我们会用三个值分别表示一段区间的从左边开始的最大子段和、从右边开始的最大子段和以及总的最大子段和,注意,在翻转的时候,我们不仅要交换左右儿子,同时也要交换当前点的左右最大子段和长度(因为序列被翻转了)

    而如果要合并的两棵树中有空树,代码中就会直接返回另一棵树而不进行down,我们考虑这样造成的影响。

    如果在(Merge)的过程中我们一路(Merge)到底,则两棵树的两条链会下传标记。一旦有一颗树变空,另一棵树的根节点就不会下传标记,但是下传标记在这里是次要的,注意上面有讲到,下传标记的同时我们会交换左右的最大子段和,而这两个值是会影响那个根节点的父亲的更新的。

    这里给出我下传翻转标记更新最大子段和的代码

    il vd Upd_r(int u){a[u].fr^=1,swap(a[u].l,a[u].r),swap(a[u].lm,a[u].rm);} 
    // 翻转 fr为标记
    a[u].lm=Max(a[lu].lm,a[lu].sum+a[u].v+a[ru].lm), //左边
    a[u].rm=Max(a[ru].rm,a[ru].sum+a[u].v+a[lu].rm), //右边
    a[u].sm=Max(Max(lu?a[lu].sm:INF,ru?a[ru].sm:INF),a[lu].rm+a[u].v+a[ru].lm); //总
    // 最大子段和
    

    可以看到,父亲的更新受到儿子的值的影响。

    所以(Merge)函数需稍作更改:

    int Merge(int x,int y){
    	down(x),down(y);
    	if(!x||!y) return x+y;
    	return a[x].d<a[y].d?a[x].r=Merge(a[x].r,y),Upd(x),x:(a[y].l=Merge(x,a[y].l),Upd(y),y);
    }
    

    2.

    (其实同理)以前写翻转的时候总是先给(Split)出的节点打上标记,在下传的时候交换它的左右儿子。

    同样是对的,但不适于最大子段和(毒瘤)...

    上面已经讲过,我们要交换的不仅仅是左右儿子,还有维护最大子段和的信息,考虑我们还要将当前(Split)出来的子树(Merge)回去,如果又碰到和空树(Merge)的情况(尴尬)不就(GG)了?

    于是我们先交换左右儿子,在下传标记的时候应交换左右儿子的左右儿子:

    il vd Upd_r(int u){a[u].fr^=1,swap(a[u].l,a[u].r),swap(a[u].lm,a[u].rm);}
    //标记下传
    if(a[u].fr) Upd_r(a[u].l),Upd_r(a[u].r),a[u].fr=0;
    //下传函数down()中写法,另在外面有一句Upd_r()!
    

    代码

    最后贴一下总代码ヾ(✿゚▽゚)ノ

    #include <iostream>
    #include <cstdio>
    #include <cctype>
    #include <cstdlib>
    #include <queue>
    #define il inline
    #define vd void
    #define INF -1000000000
    #define mn 500005
    #define Orz FULeNiLe();
    #define rep(i,x,y) for(register int i=x;i<=y;++i)
    #define drp(i,x,y) for(register int i=x;i>=y;--i)
    using namespace std;
    const int Len=2333333;
    char buf[Len],*p1=buf,*p2=buf,duf[Len],*q1=duf;
    il char gc(); il int rd(); il vd pc(char c); il vd rt(int x); il vd flush();
    template<class T> il T Max(T a,T b){return a>b?a:b;}
    template<class T> il T Min(T a,T b){return a<b?a:b;}
    int n,m,root,cnt,Cnt,p,tot,x,y,z,st[mn];
    char c;
    queue <int> tra;
    struct Treap{
        int l,r,v,s,d,fm,fr,sum,lm,rm,sm;
        il vd Upd(int u,int k){if(u) v=fm=k,sum=s*k,lm=rm=Max(0,sum),sm=Max(sum,k);}
    }a[mn];
    //fm:修改 fr:翻转 sum:求和 lm,rm,sm:子段和
    il int New(int v){
        if(tra.empty()) cnt=++Cnt;
        else cnt=tra.front(),tra.pop();
        return a[cnt]=(Treap){0,0,v,1,rand(),INF,0,v,v,v,v},cnt;
    }
    il vd Upd_r(int u){a[u].fr^=1,swap(a[u].l,a[u].r),swap(a[u].lm,a[u].rm);}
    il vd Upd(int u){
        int lu=a[u].l,ru=a[u].r;
        a[u].s=a[lu].s+a[ru].s+1,
        a[u].sum=a[lu].sum+a[ru].sum+a[u].v,
        a[u].lm=Max(a[lu].lm,a[lu].sum+a[u].v+a[ru].lm),
        a[u].rm=Max(a[ru].rm,a[ru].sum+a[u].v+a[lu].rm),
        a[u].sm=Max(Max(lu?a[lu].sm:INF,ru?a[ru].sm:INF),a[lu].rm+a[u].v+a[ru].lm);
    }
    il vd down(int u){
        if(a[u].fm!=INF) a[a[u].l].Upd(a[u].l,a[u].fm),
            a[a[u].r].Upd(a[u].r,a[u].fm),a[u].fm=INF;
        if(a[u].fr) Upd_r(a[u].l),Upd_r(a[u].r),a[u].fr=0;
    }
    il int Build(int n){int tp=0;
        rep(i,1,n){
            New(rd()); int lt=0;
            while(tp&&a[cnt].d<a[st[tp]].d) Upd(lt=st[tp--]);
            if(tp) a[st[tp]].r=cnt; a[cnt].l=lt,st[++tp]=cnt;
        }
        while(tp) Upd(st[tp--]);
        return st[1];
    }
    vd Split(int u,int k,int &x,int &y){
        if(!u) return x=y=0,void(0); down(u),
        a[a[u].l].s>=k?y=u,Split(a[u].l,k,x,a[y].l):(x=u,Split(a[u].r,k-a[a[u].l].s-1,a[x].r,y)),Upd(u);
    }
    int Merge(int x,int y){
        down(x),down(y);
        if(!x||!y) return x+y;
        return a[x].d<a[y].d?a[x].r=Merge(a[x].r,y),Upd(x),x:(a[y].l=Merge(x,a[y].l),Upd(y),y);
    }
    il vd FULeNiLe(){while(isupper(c=gc())||c=='-');}
    il vd Print(){
        rep(i,0,cnt) printf("%d l:%d r:%d sum:%d sm:%d lm:%d rm:%d s:%d
    ",i,a[i].l,a[i].r,a[i].sum,a[i].sm,a[i].lm,a[i].rm,a[i].s);
        puts("");
        printf("%d %d %d
    
    ",x,y,z);
    }
    il vd Del(int x){
        if(a[x].l) Del(a[x].l);
        tra.push(x);
        if(a[x].r) Del(a[x].r);
    }
    int main(){
        srand(19940925),n=rd(),m=rd(),root=Build(n);
        //printf("%d
    ",a[root].lm);
        while(m--){
            while(!isupper(c=gc()));// printf("ss%c ss
    ",c);
            switch(c){
                case 'I':Split(root,rd(),x,y),root=Merge(x,Merge(Build(rd()),y)); break;
                case 'D':Split(root,rd()-1,x,y),Split(y,rd(),y,z),root=Merge(x,z),Del(y); break;
                case 'R':Split(root,rd()-1,x,y),Split(y,rd(),y,z),
                         Upd_r(y),root=Merge(x,Merge(y,z)); break;
                case 'G':Orz Split(root,rd()-1,x,y),Split(y,rd(),y,z),
                         rt(a[y].sum),pc('
    '),root=Merge(x,Merge(y,z)); break;
                default:gc(); if(gc()=='K'){
                        Orz Split(root,rd()-1,x,y),Split(y,rd(),y,z),
                            a[y].Upd(y,rd()),root=Merge(x,Merge(y,z));
                        }
                    else rt(a[root].sm),pc('
    '), Orz
            }
            //Print();
            //flush();
        }
        return flush(),0;
    }
     
    il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Len,stdin),p1==p2)?-1:*p1++;}
    il int rd(){char c;
        while(!isdigit(c=gc())&&c!='-');
        int f=c=='-'?c=gc(),1:0,x=c^48;
        while(isdigit(c=gc())) x=((x+(x<<2))<<1)+(c^48);
        return f?-x:x;
    }
    il vd pc(char c){q1==duf+Len&&fwrite(q1=duf,1,Len,stdout),*q1++=c;}
    il vd rt(int x){x<0?pc('-'),x=-x:0,pc((x>=10?rt(x/10),x%10:x)+48);}
    il vd flush(){fwrite(duf,1,q1-duf,stdout),q1=duf;}
    
  • 相关阅读:
    8 -- 深入使用Spring -- 5...3 使用@CacheEvict清除缓存
    8 -- 深入使用Spring -- 5...2 使用@Cacheable执行缓存
    tomcat 的 server.xml配置文件
    WEB-INF目录与META-INF目录的作用
    一个tomcat设置多个端口,多个端口对应多个应用
    8 -- 深入使用Spring -- 5...1 启用Spring缓存
    8 -- 深入使用Spring -- 5... Spring 3.1 新增的缓存机制
    8 -- 深入使用Spring -- 4...6 AOP代理:基于注解的XML配置文件的管理方式
    eclipse中设置文件的编码格式为utf-8
    MySQL 触发器简单实例
  • 原文地址:https://www.cnblogs.com/Shallowy/p/10028281.html
Copyright © 2020-2023  润新知