伸展树模板小结(Splay Tree)
来源:互联网 发布:淘宝好货源深圳 编辑:程序博客网 时间:2024/04/28 13:21
首先推荐网上看到的四篇好文章,便于大家对于伸展树有一个直观的的了解:
伸展树的旋转和伸展操作:点击打开链接
伸展树的初步应用—解决区间问题:点击打开链接
伸展树解决区间问题:点击打开链接
伸展树结点的SIZE域的应用:点击打开链接
下面是 kuangbin 的 BZOJ 1500 维护数列的模板~
个人加上了一点自己的理解,对于模板多余的地方也有些修改~~~
//Splay_tree#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#define Key_value ch[ch[root][1]][0]//在对区间操作的时候,Key_value就是要返回的值//首先把第pos个数旋转(因为包含根节点root~)至根节点,然后把//第(pos+区间长度+1)的数旋转为根节点的右儿子,所以此时Key_value对应的子树就是要求的区间using namespace std;const int MAXN=500010;const int INF=0x3f3f3f3f;//这里是伸展树对应的结构~int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN];int root,tot1;//求和标记,翻转标记,修改的延迟标记int sum[MAXN],rev[MAXN],same[MAXN];//求最大字段和所需要的标记int lx[MAXN],rx[MAXN],mx[MAXN];//这里是移除子树后的容量池int s[MAXN],tot2;//原数组int a[MAXN];int n,q;//旋转过程中要维护的伸展树本身属性的域:size[]// 求和性质的域:sum[]// 区间翻转性质的域:rev[]// 修改相同标记的域:same[]// 最大子段和的域:lx[],rx[],mx[]void NewNode(int &r,int father,int k){ //如果删去子树有多余空出的容量,那么先使用此容量 if(tot2) r=s[tot2--]; else r=++tot1; pre[r]=father; ch[r][0]=ch[r][1]=0; key[r]=k; sum[r]=k; rev[r]=same[r]=0; lx[r]=rx[r]=mx[r]=k; size[r]=1;}//这里rev[],same[]标记为真表示父节点的对应信息还没有更新到子节点void Update_Rev(int r){ if(!r) return; swap(ch[r][0],ch[r][1]); swap(lx[r],rx[r]); //对于翻转操作不影响size[],sum[],mx[]域的信息}void Update_Same(int r,int v){ if(!r) return; key[r]=v; sum[r]=v*size[r]; lx[r]=rx[r]=mx[r]=max(v,v*size[r]); same[r]=1; //对于成段更新操作不影响size[]域~}//把子节点更新的信息更新至父节点inline void push_up(int r){ int lson=ch[r][0],rson=ch[r][1]; size[r]=size[lson]+size[rson]+1; sum[r]=sum[lson]+sum[rson]+key[r]; lx[r]=max(lx[lson],sum[lson]+key[r]+max(0,lx[rson])); rx[r]=max(rx[rson],sum[rson]+key[r]+max(0,rx[lson])); mx[r]=max(0,rx[lson])+key[r]+max(0,lx[rson]); mx[r]=max(mx[r],max(mx[lson],mx[rson]));}//把父节点的标记信息更新到子节点inline void push_down(int r){ if(same[r]) { Update_Same(ch[r][0],key[r]); Update_Same(ch[r][1],key[r]); same[r]=0; } if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; }}//以father为根建立一棵子树void Build(int &x,int l,int r,int father){ if(l>r) return; int mid=(l+r)/2; NewNode(x,father,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); push_up(x);}void Init(){ root=tot1=tot2=0; ch[root][0]=ch[root][1]=size[root]=pre[root]=0; same[root]=rev[root]=sum[root]=key[root]=0; lx[root]=rx[root]=mx[root]=-INF; //建立第一个结点 NewNode(root,0,-1); //建立最后一个结点 NewNode(ch[root][1],root,-1); for(int i=0;i<n;i++) scanf("%d",&a[i]); Build(Key_value,0,n-1,ch[root][1]); push_up(ch[root][1]); push_up(root);}/***************************************************////核心代码,维护伸展树旋转过程中的结构不被破坏//将x结点设置为p结点的kind儿子inline void Link(int p,int kind,int x){ ch[p][kind]=x; pre[x]=p;}//旋转方式,0为左旋,1为右旋//右儿子左旋,左儿子右旋inline void Rotate(int x,int kind){ int p=pre[x]; //每次对一个结点旋转的时候,首先要把其父节点的信息更新 //然后把此节点的信息更新至子节点 //维护了延迟标记的完整性 push_down(p); push_down(x); //重新确定3对父子关系 //父亲-种类-儿子 //这里儿子变了的有:pre[p],p,x //这里父亲变了的有:ch[x][kind],x,p Link(p,!kind,ch[x][kind]); Link(pre[p],ch[pre[p]][1]==p,x); Link(x,kind,p); //原父节点的儿子变了,所以需要更新信息 push_up(p);}//Splay调整,将r结点调整到goal下面void Splay(int r,int goal){ push_down(r); while(pre[r]!=goal) { int p=pre[r]; if(pre[p]==goal) Rotate(r,ch[pre[r]][0]==r); else { //kind表示该节点是左儿子还是右儿子 //左儿子返回1,右儿子返回0 int kind=ch[pre[p]][0]==p; ch[p][kind]==r?Rotate(r,!kind):Rotate(p,kind); Rotate(r,kind); } } //最后更新旋转结点 push_up(r); if (goal==0) root=r;}/***************************************************///对于伸展树来说,中序遍历的结果就是原数组//所以利用size[]域可求得区间解决k_th问题int Get_kth(int r,int k){ push_down(r); int t=size[ch[r][0]]+1; if(t==k) return r; if(t>k) return Get_kth(ch[r][0],k); else return Get_kth(ch[r][1],k-t);}void Insert(int pos,int tot){ for(int i=0;i<tot;i++) scanf("%d",&a[i]); Splay(Get_kth(root,pos+1),0); Splay(Get_kth(root,pos+2),root); Build(Key_value,0,tot-1,ch[root][1]); push_up(ch[root][1]); push_up(root);}//删除子树void erase(int r){ if(!r) return; s[++tot2]=r; erase(ch[r][0]); erase(ch[r][1]);}void Delete(int pos,int tot){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); erase(Key_value); Key_value=0; push_up(ch[root][1]); push_up(root);}//将第pos个数开始的连续tot个数修改为cvoid Make_Same(int pos,int tot,int c){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); Update_Same(Key_value,c); push_up(ch[root][1]); push_up(root);}//将第pos个数开始的连续tot个数进行反转void Reverse(int pos,int tot){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); Update_Rev(Key_value); push_up(ch[root][1]); push_up(root);}//得到第pos个数开始的连续tot个数的和int Get_Sum(int pos,int tot){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); return sum[Key_value];}//得到第pos个数开始的tot个数中最大的子段和int Get_MaxSum(int pos,int tot){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); return mx[Key_value];}
下面是我对于模板增加的地方~
成段修改增加或者减少~
测试地点:学校oj(Orz,Orz) 点击打开链接
///Splay_tree#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#define Key_value ch[ch[root][1]][0]using namespace std;typedef long long LL;const int MAXN=100010;int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN];int root,tot1;int mod[MAXN];LL sum[MAXN];int a[MAXN];int n,q;void NewNode(int &r,int father,int k){ r=++tot1; pre[r]=father; ch[r][0]=ch[r][1]=0; key[r]=k; sum[r]=k; mod[r]=0; size[r]=1;}void Update_modify(int r,int v){ if(!r) return; key[r]+=v; sum[r]+=v*size[r]; mod[r]+=v;}//把子节点更新的信息更新至父节点inline void push_up(int r){ int lson=ch[r][0],rson=ch[r][1]; size[r]=size[lson]+size[rson]+1; sum[r]=sum[lson]+sum[rson]+key[r];}//把父节点的标记信息更新到子节点inline void push_down(int r){ if(mod[r]) { Update_modify(ch[r][0],mod[r]); Update_modify(ch[r][1],mod[r]); mod[r]=0; }}//以father为根建立一棵子树void Build(int &x,int l,int r,int father){ if(l>r) return; int mid=(l+r)/2; NewNode(x,father,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); push_up(x);}void Init(){ root=tot1=0; ch[root][0]=ch[root][1]=size[root]=pre[root]=0; mod[root]=sum[root]=key[root]=0; //建立第一个结点 NewNode(root,0,-1); //建立最后一个结点 NewNode(ch[root][1],root,-1); for(int i=0;i<n;i++) scanf("%d",&a[i]); Build(Key_value,0,n-1,ch[root][1]); push_up(ch[root][1]); push_up(root);}inline void Link(int p,int kind,int x){ ch[p][kind]=x; pre[x]=p;}inline void Rotate(int x,int kind){ int p=pre[x]; push_down(p); push_down(x); Link(p,!kind,ch[x][kind]); Link(pre[p],ch[pre[p]][1]==p,x); Link(x,kind,p); push_up(p);}void Splay(int r,int goal){ push_down(r); while(pre[r]!=goal) { int p=pre[r]; if(pre[p]==goal) { Rotate(r,ch[pre[r]][0]==r); } else { int kind=ch[pre[p]][0]==p; ch[p][kind]==r?Rotate(r,!kind):Rotate(p,kind); Rotate(r,kind); } } push_up(r); if (goal==0) root=r;}int Get_kth(int r,int k){ push_down(r); int t=size[ch[r][0]]+1; if(t==k) return r; if(t>k) return Get_kth(ch[r][0],k); else return Get_kth(ch[r][1],k-t);}//成段修改增加值void modify(int pos,int tot,int c){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); Update_modify(Key_value,c); push_up(ch[root][1]); push_up(root);}//查询区间和LL Get_Sum(int pos,int tot){ Splay(Get_kth(root,pos),0); Splay(Get_kth(root,pos+tot+1),root); return sum[Key_value];}int main(){ int l,r,c,nofcase=1; char op; while(scanf("%d%d",&n,&q)!=EOF) { Init(); printf("Case %d:\n",nofcase++); while(q--) { cin>>op; scanf("%d%d",&l,&r); if(op=='C') { scanf("%d",&c); modify(l,r-l+1,c); } else printf("%I64d\n",Get_Sum(l,r-l+1)); } } return 0;}
阅读全文
0 0
- 伸展树模板小结(Splay Tree)
- 伸展树(Splay Tree)小结
- 伸展树(Splay tree)学习小结
- 伸展树(Splay tree)学习小结
- splay 伸展树小结
- Splay Tree(伸展树)
- Splay Tree 伸展树
- 伸展树splay tree
- Splay Tree(伸展树)
- splay tree(伸展树)
- 伸展树(Splay tree)
- 伸展树(Splay tree)
- Splay Tree(伸展树)
- splay - tree 伸展树
- 伸展树Splay Tree
- Splay Tree(伸展树)
- 伸展树splay tree
- 伸展树(splay tree)
- 机器学习算法简介
- qt5.8交叉编译移植到arm开发板上过程
- 如何安装和使用SimSo模拟器
- 整合spring cloud云架构
- SpringMVC基础-7-文件上传处理
- 伸展树模板小结(Splay Tree)
- Oracle Net Manager 服务命名配置以及用PL/SQL 登陆数据库
- 20171025
- 简单的实现微信获取openid
- 大偏移量下Redis、MongoDB分页/排名性能比较
- openat用法
- 每日一练 20171102
- 要到2062年,阿里云才能追平AWS?!
- 集团型公司人力资源部云上转型已成必然,SaaS服务依旧引领云计算市场