发现自己以前就没有真正理解透彻过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;}