转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
N年前用线段树做的,比较简单,可以当作线段树懒惰标记的练习。
重新用Splay tree写,有点小题大作,而且代码长,效率低,不过当作Splay练手不错。
区间处理,也是splay的强项。一开始建树的时候,将键值设为下标,插入到树中,保证有序,总是出错。
后来采用HH的做法,直接建树,中间结点为父结点,区间左端为左子树,区间右端为右子树,这样就保证始终有序,之后的旋转不改变。
Splay的区间处理,对于[l,r],将l-1旋转至根,将r+1旋转至根的右孩子,那么根的右孩子的左子树就全为[l,r]中的结点,便可以对区间进行有效处理。
同样区间更新用到懒惰标记。
区间可能靠近两端点,当减1加1时可能溢出,不方便处理,可以在两端加入冗余结点。
以下为Splay tree
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define N 100005 #define inf 1<<29 #define LL long long #define Key_value ch[ch[root][1]][0] using namespace std; int pre[N],key[N],ch[N][2],root,tot1; //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量 int size[N],s[N],tot2,val[N]; //分别表示子树规模,内存池,内存池容量 int add[N]; LL sum[N]; //延迟标记,子树结点和 int n,q; //debug部分copy from hh void Treaval(int x) { if(x) { Treaval(ch[x][0]); printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2d \n",x,ch[x][0],ch[x][1],pre[x],size[x],val[x],sum[x]); Treaval(ch[x][1]); } } void debug() {printf("%d\n",root);Treaval(root);} //以上Debug //新建一个结点 void NewNode(int &r,int father,int k){ if(tot2) r=s[--tot2]; else r=++tot1; pre[r]=father; val[r]=k; sum[r]=k; add[r]=0; size[r]=1; ch[r][0]=ch[r][1]=0; //左右孩子为空 } //将延迟标记更新到孩子结点 void Push_Down(int r){ if(add[r]){ val[r]+=add[r]; add[ch[r][0]]+=add[r]; add[ch[r][1]]+=add[r]; sum[ch[r][0]]+=(LL)add[r]*size[ch[r][0]]; sum[ch[r][1]]+=(LL)add[r]*size[ch[r][1]]; add[r]=0; } } //通过孩子结点更新父结点 void Push_Up(int r){ size[r]=size[ch[r][0]]+size[ch[r][1]]+1; sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+val[r]+add[r]; } //旋转,kind为1为右旋,kind为0为左旋 void Rotate(int x,int kind){ int y=pre[x]; Push_Down(x); Push_Down(y); //类似SBT,要把其中一个分支先给父节点 ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; //如果父节点不是根结点,则要和父节点的父节点连接起来 if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } //Splay调整,将根为r的子树调整为goal void Splay(int r,int goal){ Push_Down(r); while(pre[r]!=goal){ //父节点即是目标位置,goal为0表示,父节点就是根结点 if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r); else{ int y=pre[r]; int kind=ch[pre[y]][0]==y; //两个方向不同,则先左旋再右旋 if(ch[y][kind]==r){ Rotate(r,!kind); Rotate(r,kind); } //两个方向相同,相同方向连续两次 else{ Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); //更新根结点 if(goal==0) root=r; } //把第k位的数转到goal下边 void RotateTo(int k,int goal) { int r=root; Push_Down(r); while(size[ch[r][0]]!=k){ if(k<size[ch[r][0]]){ r=ch[r][0]; } else { k-=(size[ch[r][0]]+1); r=ch[r][1]; } Push_Down(r); } Splay(r,goal); } int Insert(int k){ int r=root; while(ch[r][key[r]<k]) r=ch[r][key[r]<k]; NewNode(ch[r][k>key[r]],r,k); //将新插入的结点更新至根结点 //Push_Up(r); Splay(ch[r][k>key[r]],0); return 1; } //找前驱,即左子树的最右结点 int get_pre(int x){ int tmp=ch[x][0]; if(tmp==0) return inf; while(ch[tmp][1]) tmp=ch[tmp][1]; return key[x]-key[tmp]; } //找后继,即右子树的最左结点 int get_next(int x){ int tmp=ch[x][1]; if(tmp==0) return inf; while(ch[tmp][0]) tmp=ch[tmp][0]; return key[tmp]-key[x]; } //查询[l,r]之间的和 LL Query(int l,int r){ RotateTo(l-1,0); RotateTo(r+1,root); return sum[Key_value]; } //更新 void Update(int l,int r){ int k; scanf("%d",&k); RotateTo(l-1,0); RotateTo(r+1,root); add[Key_value]+=k; sum[Key_value]+=size[Key_value]*k; } int a[N]; //建树,中间结点先建立,然后分别对区间两端在左右子树建立 void BulidTree(int &x,int l,int r,int father){ if(l>r) return; int mid=(l+r)/2; NewNode(x,father,a[mid]); if(l<mid) BulidTree(ch[x][0],l,mid-1,x); if(r>mid) BulidTree(ch[x][1],mid+1,r,x); Push_Up(x); } void Init(){ for(int i=0;i<n;i++) scanf("%d",&a[i]); ch[0][0]=ch[0][1]=pre[0]=size[0]=0; add[0]=sum[0]=0; root=tot1=0; NewNode(root,0,-1); NewNode(ch[root][1],root,-1); //头尾各加入一个空位 size[root]=2; BulidTree(Key_value,0,n-1,ch[root][1]); //让所有数据夹在两个-1之间 Push_Up(ch[root][1]); Push_Up(root); } int main(){ while(scanf("%d%d",&n,&q)!=EOF){ Init(); while(q--){ char str[10]; int x,y; scanf("%s%d%d",str,&x,&y); if(str[0]=='Q') printf("%lld\n",Query(x,y)); else Update(x,y); } } return 0; }
#include<iostream> #include<cstdio> using namespace std; struct line{ int left,right,mid; int add; long long sum; }L[500005]; long long a[500005],n; long long bulid(int step,int l,int r){ L[step].left=l; L[step].right=r; L[step].mid=(l+r)/2; L[step].add=0; if(l==r) return L[step].sum=a[l]; return L[step].sum=bulid(2*step,l,(l+r)/2)+bulid(2*step+1,(l+r)/2+1,r); } void update(int step,long long x,long long y,long long z){ if(x <= L[step].left && y >= L[step].right) { L[step].add += z; L[step].sum += (long long )(L[step].right-L[step].left+1)*z; return ; } if(L[step].add) { L[2*step].sum += (long long )(L[step].mid-L[step].left+1)*L[step].add; L[2*step+1].sum+=(long long )(L[step].right-L[step].mid)*L[step].add; L[2*step].add += L[step].add; L[2*step+1].add+=L[step].add; L[step].add = 0; } if(x <= L[step].mid) update(2*step,x,y,z); if(L[step].mid<y) update(2*step+1,x,y,z); L[step].sum=L[2*step].sum +L[2*step+1].sum; } long long query(int step,long long x,long long y){ if(L[step].left==x&&L[step].right==y) return L[step].sum; if(L[step].add) { L[2*step].sum += (long long )(L[step].mid-L[step].left+1) * L[step].add; L[2*step+1].sum += (long long )(L[step].right-L[step].mid) * L[step].add; L[2*step].add+=L[step].add; L[2*step+1].add+=L[step].add; L[step].add=0; } if(L[step].mid<x) return query(2*step+1,x,y); else if(L[step].mid>=y) return query(2*step,x,y); else return query(2*step,x,L[step].mid)+query(2*step+1,L[step].mid+1,y); } int main(){ int m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); char str[5]; bulid(1,1,n); while(m--){ scanf("%s",str); if(str[0]=='C'){ long long x,y,z; scanf("%lld%lld%lld",&x,&y,&z); update(1,x,y,z); } else { long long x,y; scanf("%lld%lld",&x,&y); printf("%lld\n",query(1,x,y)); } } // system("pause"); return 0; }