poj 3468 Splay 树
大二上的時候。寫過一個AVL的操作演示,今天一看Splay。發(fā)現(xiàn)和AVL事實上一樣,加上線段樹的基礎(chǔ),懶惰標記什么都知道。學起來輕松很多哦
我參考的模板來自這里 ?http://blog.csdn.net/u013480600/article/list/2
里面有大量的ch[r][0] ch[r][1]等 我建議用宏定義代替,寫的時候方括號少打了非常多,等做的題多得時候,我再把自己使用的模板發(fā)來
?
?
?
//內(nèi)存池、內(nèi)存池容量(這題用不到。假設(shè)有刪除操作。內(nèi)存不夠能夠這樣 //s中存的是被回收的內(nèi)存,從后面能夠看到,tot2不為0的時候優(yōu)先使用s[tot2]=x中的 //pre[x],ch[x],size[x]等, //否則就使用tot1++之后產(chǎn)生的數(shù)x的相應(yīng)位置 int a[MAXN];//初始時的數(shù)組,建樹時用 int n,q; void newnode(int &r,int father, int k)//r必須是& { if(tot2)r=s[tot2--];//取得時候tot2--,存++tot2 else r=++tot1; pre[r]=father; sz[r]=1; key[r]=k; add[r]=sum[r]=0; ch[r][0]=ch[r][1]=0; } void updateadd(int r, int av) { if(!r)return;//?
? add[r]+=av; key[r]+=av; sum[r]+=(ll)av*sz[r]; } //通過孩子結(jié)點更新父親結(jié)點 void pushup(int r) { sz[r]=sz[ls(r)]+sz[rs(r)]+1; sum[r]=sum[ls(r)]+sum[rs(r)]+key[r]; } //將延遲標記更新到孩子結(jié)點 void pushdown(int r) { if(add[r]) { updateadd(ls(r),add[r]); updateadd(rs(r),add[r]); add[r]=0; } } //建樹區(qū)間[l,r]。先建立中間結(jié)點,再建兩端。 //注意和線段樹的差別 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); pushup(x); } //初始化。前后各加一個king結(jié)點 void Init() { for(int i=1;i<=n;i++) scanf("%d",&a[i]); root=tot1=tot2=0; ls(root)=rs(root)=pre[root]=sz[root]=add[root]=sum[root]=key[root]=0; newnode(root, 0, -1);//root'sfatheris -1 newnode(rs(root),root,-1);//頭尾各插入一個空 build(key_value, 1, n, rs(root)); pushup(rs(root)); pushup(root); } //旋轉(zhuǎn),0為左旋。1為右旋 該部分基本固定 void rota(int x, int kind) { int y=pre[x]; pushdown(y); pushdown(x);//必須先把y的標記向下傳遞。在傳x ch[y][!kind]=ch[x][kind],pre[ch[x][kind]]=y; if(pre[y])//??y的父節(jié)點不是root ch[pre[y]][ rs(pre[y])==y ]=x;//僅僅能對這句牛逼的代碼說聲我屮艸芔茻 pre[x]=pre[y]; ch[x][kind]=y,pre[y]=x; pushup(y);//維護y結(jié)點 x節(jié)點的信息不用PushUp嗎?
能夠證明這里除了x節(jié)點維護的信息不正確外,其它全部點信息都正確 //Push_Up(x);//這個能夠不寫,詳見Crash:運用伸展樹解決數(shù)列維護問題 論文,可是寫了也不會多多少時間消耗 } void Splay(int r, int goal)//將r調(diào)整到goal以下 { pushdown(r);// 離開之前把懶惰標記的信息傳遞 while(pre[r]!=goal) { if(pre[pre[r]]==goal) rota(r, ch[pre[r]][0]==r);//r在左子樹。右旋 else { int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r)//之字形旋轉(zhuǎn) {//y在其父節(jié)點的左子樹上,r在y的右子樹上 //或者y在其父節(jié)點的右子樹。r在y左子樹 rota(r, !kind); rota(r, kind); } else //一字型旋轉(zhuǎn) { rota(y, kind);//注意這里是y啊 rota(r, kind); } } } pushup(r); if(goal==0)root=r; } //得到第k個結(jié)點編號 int getkth(int r, int k) { pushdown(r); int t=sz[ls(r)]+1; if(t==k)return r; if(t>k)return getkth(ls(r),k);//在左子樹第k個 else return getkth(rs(r), k-t);//在右子樹第k-t個 } //得到以r為根的第一個結(jié)點--最左邊的節(jié)點編號 int getmin(int r) { pushdown(r); while(ls(r)) { r=ls(r); pushdown(r); } return r; } //得到以r為根的最后一個結(jié)點--最右邊的編號 int getmax(int r) { pushdown(r); while(rs(r)) { r=rs(r); pushdown(r); } return r; } void addv(int l, int r, int d) { Splay(getkth(root,l),0); Splay(getkth(root,r+2),root); updateadd(key_value, d); pushup(rs(root)); pushup(root); } ll Querysum(int l, int r) { Splay(getkth(root, l),0);//第l個點到根結(jié)點 Splay(getkth(root, r+2), root);//第r+2個點到根結(jié)點的右孩子 return sum[key_value]; } int main() { //freopen("poj3468.txt","r",stdin); int q; while(~scanf("%d%d", &n, &q)) { Init(); while(q--) { char op[30]; int x,y,z; scanf("%s",op); if(op[0]=='Q') { scanf("%d%d",&x,&y); printf("%lld\n",Querysum(x,y)); } else { scanf("%d%d%d",&x,&y,&z); addv(x,y,z); } } } return 0; }
轉(zhuǎn)載于:https://www.cnblogs.com/lxjshuju/p/6835498.html
總結(jié)
以上是生活随笔為你收集整理的poj 3468 Splay 树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 基础知识复习(四)---数组
- 下一篇: 09_ServletContext介绍