標籤:blog http 使用 os io for art 問題
大二上的時候,寫過一個AVL的操作示範,今天一看Splay,發現和AVL其實一樣,加上線段樹的基礎,懶惰標記什麼都知道,學起來輕鬆許多哦
我參考的模板來自這裡 http://blog.csdn.net/u013480600/article/list/2
裡面有大量的ch[r][0] ch[r][1]等 我建議用宏定義取代,寫的時候方括弧少打了很多,等做的題多得時候,我再把自己使用的模板發來
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;/*===============================Splay樹模板1、tot1,tot2都是從1開始2、================================*/#define key_value ch[ch[root][1]][0]#define ls(r) ch[r][0]#define rs(r) ch[r][1]#define ll long long#define repe(i,s,e) for(int i=s;i<=e;i++)#define rep(i,s,e) for(int i=s;i<e;i++)const int MAXN=100000 +10;int pre[MAXN],ch[MAXN][2],sz[MAXN],root,tot1;// 父節點、左右孩子(0為左,同時0為左旋)、子樹規模、根節點、結點數量int key[MAXN];//結點的值int add[MAXN];ll sum[MAXN];//sum[i]=v 以i為root的樹的和,int s[MAXN],tot2;/////??????//記憶體池、記憶體池容量(這題用不到,如果有刪除操作,記憶體不夠可以這樣//s中存的是被回收的記憶體,從後面可以看到,tot2不為0的時候優先使用s[tot2]=x中的//pre[x],ch[x],size[x]等,//否則就使用tot1++之後產生的數x的對應位置int a[MAXN];//初始時的數組,建樹時用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];}//通過孩子結點更新父親結點void pushup(int r){ sz[r]=sz[ls(r)]+sz[rs(r)]+1; sum[r]=sum[ls(r)]+sum[rs(r)]+key[r];}//將延遲標記更新到孩子結點void pushdown(int r){ if(add[r]) { updateadd(ls(r),add[r]); updateadd(rs(r),add[r]); add[r]=0; }}//建樹區間[l,r],先建立中間結點,再建兩端,//注意和線段樹的區別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結點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);}//旋轉,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的父節點不是root ch[pre[y]][ rs(pre[y])==y ]=x;//只能對這句牛逼的代碼說聲我屮艸芔茻 pre[x]=pre[y]; ch[x][kind]=y,pre[y]=x; pushup(y);//維護y結點 x節點的資訊不用PushUp嗎?可以證明這裡除了x節點維護的資訊不對外,其他所有點資訊都正確 //Push_Up(x);//這個可以不寫,詳見Crash:運用伸展樹解決數列維護問題 論文,但是寫了也不會多多少時間消耗}void Splay(int r, int goal)//將r調整到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)//之字形旋轉 {//y在其父節點的左子樹上,r在y的右子樹上 //或者y在其父節點的右子樹,r在y左子樹 rota(r, !kind); rota(r, kind); } else //一字型旋轉 { rota(y, kind);//注意這裡是y啊 rota(r, kind); } } } pushup(r); if(goal==0)root=r;}//得到第k個結點編號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為根的第一個結點--最左邊的節點編號int getmin(int r){ pushdown(r); while(ls(r)) { r=ls(r); pushdown(r); } return r;}//得到以r為根的最後一個結點--最右邊的編號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個點到根結點 Splay(getkth(root, r+2), root);//第r+2個點到根結點的右孩子 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;}