N年前用線段樹做的,比較簡單,可以當作線段樹懶惰標記的練習。
重新用Splay tree寫,有點小題大作,而且代碼長,效率低,不過當作Splay練手不錯。
區間處理,也是splay的強項。一開始建樹的時候,將鍵值設為下標,插入到樹中,保證有序,總是出錯。
後來采用HH的做法,直接建樹,中間結點為父結點,區間左端為左子樹,區間右端為右子樹,這樣就保證始終有序,之後的旋轉不改變。
Splay的區間處理,對於[l,r],將l-1旋轉至根,將r+1旋轉至根的右孩子,那麼根的右孩子的左子樹就全為[l,r]中的結點,便可以對區間進行有效處理。
同樣區間更新用到懶惰標記。
區間可能靠近兩端點,當減1加1時可能溢出,不方便處理,可以在兩端加入冗余結點。
以下為Splay tree
[cpp]
#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;
}
以下為Segment tree
[cpp]
#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 www.2cto.com
{
long long x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,x,y));
}
}
// system("pause");
return 0;
}