程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> POJ 3468 A Simple Problem with Integers Splay tree&S

POJ 3468 A Simple Problem with Integers Splay tree&S

編輯:C++入門知識

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; 


作者:ACM_cxlove

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved