平衡树 fhqTreap 区间操作
Posted water-radish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了平衡树 fhqTreap 区间操作相关的知识,希望对你有一定的参考价值。
//Treap fhq版(不旋转) //此模板为平衡树维护区间操作的模板 //注:在区间操作中split()标准变为子树大小 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #define MAXN 500001 using namespace std; int n,m,a[MAXN],cnt,size,root; int son[MAXN][2],siz[MAXN],val[MAXN],sum[MAXN],rd[MAXN]; int lazy_revise[MAXN],lazy_reverse[MAXN];//记录当前节点是否需要修改或翻转 int tmx[MAXN],lmx[MAXN],rmx[MAXN];//求最大子段和专用数组 //当前节点最大子段和,左儿子最大子段和,右儿子最大子段和 queue<int> trashcan;//回收节点重复利用 inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } void update(int x) { if(son[x][0]&&son[x][1]) { siz[x]=siz[son[x][0]]+siz[son[x][1]]+1; sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x]; tmx[x]=max(tmx[son[x][0]],tmx[son[x][1]]); tmx[x]=max(tmx[x],rmx[son[x][0]]+val[x]+lmx[son[x][1]]); lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]+lmx[son[x][1]]); rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]+rmx[son[x][0]]); } if(son[x][0]&&!son[x][1]) { siz[x]=siz[son[x][0]]+1; sum[x]=sum[son[x][0]]+val[x]; tmx[x]=max(tmx[son[x][0]],rmx[son[x][0]]+val[x]); lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]); lmx[x]=max(0,lmx[x]); rmx[x]=max(0,val[x]+rmx[son[x][0]]); } if(!son[x][0]&&son[x][1]) { siz[x]=siz[son[x][1]]+1; sum[x]=sum[son[x][1]]+val[x]; tmx[x]=max(tmx[son[x][1]],lmx[son[x][1]]+val[x]); rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]); rmx[x]=max(0,rmx[x]); lmx[x]=max(0,lmx[son[x][1]]+val[x]); } if(!son[x][0]&&!son[x][1]) { siz[x]=1,sum[x]=tmx[x]=val[x]; lmx[x]=rmx[x]=max(val[x],0); } } int new_node(int k) { int x=0; if(!trashcan.empty()) { x=trashcan.front(); trashcan.pop(); } else x=++cnt; son[x][0]=son[x][1]=0; lazy_reverse[x]=0; lazy_revise[x]=INF; rd[x]=rand(); siz[x]=1; val[x]=sum[x]=tmx[x]=k; lmx[x]=rmx[x]=max(k,0); return x; } int build(int *data,int k)//类似于笛卡尔树的建树方式 //笛卡尔树的建树方式如下(笛卡尔树val值满足小根堆 随机数值满足二叉查找树) //前提新节点插入顺序必须val值从小到大插入 以保证新入节点val值必比原有节点大 //栈内存极右链 即从栈底到栈顶存储根节点 根节点的右儿子 根节点的右儿子的右儿子 显然随机数值从栈顶到栈底递减 //进入一个新节点后 从栈顶开始搜索 找到栈中第一个比新入节点随机数值小的数 //由于随机数值小 因此新入节点是找到的节点的儿子 又由于val值大 因此新入节点是找到的节点的右儿子 //又因为新入节点的val值大于找到的节点的右儿子的val值 因此原右儿子成为新入节点的左儿子 //由于原右儿子即为找到的节点在栈内上方的元素 因此把此元素修改至左儿子后 把此元素及其上方元素出栈即可 //由于栈中存极右链 因此找到的节点将在栈中位于找到的节点的上方 即替代原右儿子的位置 //直至所有点均插入即可 { int now=0,pre=0;//当前节点,找到的节点的原右儿子 static int sta[MAXN],top=0; //static:静态变量 其在定义时系统将自动将其初始化为0 另外在调用完后不会直接销毁 而会在下次重新定义同名静态变量时重新将以前定义过的变量调用 同时保留上次调用完后变量内存放的数据 for(int i=1;i<=k;i++) { now=new_node(data[i]); pre=0; while(top&&rd[sta[top]]>rd[now])//如果栈内有元素且未找到比当前节点随机数值小的数 { update(sta[top]);//更新出栈节点 pre=sta[top];//记录右儿子 sta[top--]=0;//先出栈再top-- } if(top)//如果栈内还有元素且找到比当前节点随机数值小的数 son[sta[top]][1]=now;//新入节点成为找到的节点的右儿子 son[now][0]=pre;//原右儿子成为新入节点的左儿子 sta[++top]=now;//新入节点入栈 } while(top)//栈内还有元素 update(sta[top--]);//全部更新 return sta[1];//栈底元素即为根节点 } void trash(int x) { if(!x) return; trashcan.push(x); trash(son[x][0]); trash(son[x][1]); } void change(int x,int k) { val[x]=k; sum[x]=siz[x]*k; lmx[x]=rmx[x]=max(sum[x],0);//若区间内节点权值全为负数那么不如不取 tmx[x]=max(sum[x],val[x]);//对于当前节点sum()多了一种选择即节点自己的权值 lazy_revise[x]=k;//标记修改值便于以后pushdown()操作 } void flip(int x) { swap(son[x][0],son[x][1]); swap(lmx[x],rmx[x]); lazy_reverse[x]^=1;//若之前就需要翻转 那么再翻转即恢复原状态 } void pushdown(int x)//区间操作特殊函数 用于处理两个lazy() { if(lazy_revise[x]!=INF) { if(son[x][0]) change(son[x][0],lazy_revise[x]); if(son[x][1]) change(son[x][1],lazy_revise[x]); } if(lazy_reverse[x]) { if(son[x][0]) flip(son[x][0]); if(son[x][1]) flip(son[x][1]); } lazy_revise[x]=INF; lazy_reverse[x]=0; } void split(int now,int k,int &x,int &y) { if(!now) { x=y=0; return; } pushdown(now); if(siz[son[now][0]]>=k)//待操作区间全部在左子树 y=now,split(son[now][0],k,x,son[now][0]); else x=now,split(son[now][1],k-siz[son[now][0]]-1,son[now][1],y); update(now); } int merge(int x,int y) { if(x) pushdown(x); if(y) pushdown(y); if(!x||!y) return x+y; if(rd[x]<rd[y]) { son[x][1]=merge(son[x][1],y); update(x); return x; } else { son[y][0]=merge(x,son[y][0]); update(y); return y; } } void insert()//插入 { int pos=read(),len=read(),x,y; int datas[MAXN]; for(int i=1;i<=len;i++) datas[i]=read(); int rt=build(datas,len);//把新节点先处理成一棵Treap split(root,pos,x,y);//沿插入位置分成两棵Treap root=merge(merge(x,rt),y);//把新Treap直接接在原Treap上 } void del()//删除 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要修改的节点都分给y1的Treap split(y1,len,x2,y2);//把所有要删除的节点都分给x2的Treap 此时x2的Treap上只有需删除的节点 root=merge(x1,y2);//把剩余的Treap合并 trash(x2);//回收节点,节省空间 } void revise()//修改 { int pos=read(),len=read(),k=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要修改的节点都分给y1的Treap split(y1,len,x2,y2);//把所有要修改的节点都分给x2的Treap 此时x2的Treap上只有需修改的节点 change(x2,k);//只需修改x2的Treap的根节点即x2 因为合并时pushdown()会完成剩余修改 root=merge(x1,merge(x2,y2));//把修改过的Treap同原Treap合并 } void reverse()//翻转 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所有要翻转的节点都分给y1的Treap split(y1,len,x2,y2);//把所有要翻转的节点都分给x2的Treap 此时x2的Treap上只有需翻转的节点 flip(x2);//只需将x2的Treap的根节点即x2的两个儿子翻转 因为合并时pushdown()会完成剩余翻转 root=merge(x1,merge(x2,y2));//把翻转过的Treap同原Treap合并 } void get_sum()//区间权值和 { int pos=read(),len=read(),x1,y1,x2,y2; split(root,pos-1,x1,y1);//把所求区间分给y1的Treap split(y1,len,x2,y2);//把所求区间分给x2的Treap 此时x2的Treap即为所求区间 printf("%d ",sum[x2]); root=merge(x1,merge(x2,y2));//把所求区间同原Treap合并 } void sub_sum()//求整个序列内的最大子段和 { printf("%d ",tmx[root]); } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); root=build(a,n); for(int i=1;i<=m;i++) { char s[10]; scanf("%s",s); if(s[0]==‘I‘) insert(); if(s[0]==‘D‘) del(); if(s[0]==‘M‘&&s[2]==‘K‘) revise(); if(s[0]==‘R‘) reverse(); if(s[0]==‘G‘) get_sum(); if(s[0]==‘M‘&&s[2]==‘X‘) sub_sum(); } return 0; }
以上是关于平衡树 fhqTreap 区间操作的主要内容,如果未能解决你的问题,请参考以下文章