关于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\n",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\n\n",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\n",a[root].lm);
    while(m--){
        while(!isupper(c=gc()));// printf("ss%c ss\n",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('\n'),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('\n'), 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;}
posted @ 2018-11-27 19:01  Shallowy  阅读(323)  评论(0编辑  收藏  举报