当前位置:编程学习 > C/C++ >>

POJ 3468 A Simple Problem with Integers Splay tree&Segment tree

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]; 补充:软件开发 , C++ ,

CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,