标签:
1 /****************************************** 2 数据结构: 3 Splay_Tree,伸展树; 4 5 详见我的另一篇博客 http://www.cnblogs.com/wyj-jenny/p/5771561.html 6 7 性质: 8 伸展树是二叉查找树的一种改进; 9 与二叉查找树一样,伸展树也具有有序性; 10 即伸展树中的每一个节点x都满足: 11 该节点左子树中的每一个元素都小于x; 12 而其右子树中的每一个元素都大于x; 13 与普通二叉查找树不同的是,伸展树可以自我调整; 14 15 特点: 16 伸展树并不是严格意义上的平衡树; 17 也还是极有可能退化成线性结构,但伸展操作能使它的每一次操作近似(logn); 18 19 伸展操作: 20 伸展操作和平衡树的保持平衡是类似的; 21 只不过他不要求保持平衡,只是相应的旋转; 22 旋转有三种情况要处理: 23 (1)Zig或Zag(节点x的父节点y是根节点) 24 (2)Zig-Zig或Zag-Zag(节点x的父节点y不是根节点,且x与y同时是各自父节点的左孩子或者同时是各自父节点的右孩子) 25 (3)Zig-Zag或Zag-Zig(节点x的父节点y不是根节点,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子) 26 即一字型旋转和之字型旋转; 27 28 优势: 29 能快速定位一个区间[l,r],并且能将区间进行删除、旋转操作; 30 将第l-1个结点旋转至根(之前的Splay操作),将第r+1个结点旋转至根的右孩子; 31 由于伸展树的本质还是二叉搜索树,则根据二叉查找树的性质可以知道; 32 在这两个结点之间,也是根的右孩子的左子树就包括节点[l,r]; 33 即很快定位了区间[l,r],如果需要删除,直接把子树拿走即可; 34 35 算法测试: 36 bzoj 3212 [PKU3468(A Simple Problem with Integers)] 37 http://www.lydsy.com/JudgeOnline/problem.php?id=3212 38 39 题目大意: 40 Q a b :查询区间[a,b]的和; 41 C a b x : 更新区间[a,b],区间所有值加上x; 42 *******************************************/ 43 #include<iostream> 44 #include<cstring> 45 #include<cstdio> 46 #include<algorithm> 47 using namespace std; 48 49 #define Key_value ch[ch[root][1]][0]//进行各种操作的区间 50 51 const int INF=0xffffff; 52 const int N=100010; 53 typedef long long LL; 54 55 int ch[N][2];//左右孩子(0为左孩子,1为右孩子) 56 int pre[N];//父结点 57 int key[N];//数据域 58 int size[N];//树的规模 59 int val[N]; 60 int add[N]; 61 int a[N];//结点元素 62 LL sum[N];//子树结点和 63 int root; //根结点 64 int tot;//结点数量 65 int n,q; 66 67 void Push_Up(int u)//通过孩子结点更新父结点 68 { 69 size[u]=size[ch[u][0]]+size[ch[u][1]]+1; 70 sum[u]=sum[ch[u][0]]+sum[ch[u][1]]+val[u]+add[u]; 71 } 72 73 void Push_Down(int u)//将延迟标记更新到孩子结点 74 { 75 if(add[u]) 76 { 77 val[u]+=add[u]; 78 add[ch[u][0]]+=add[u]; 79 add[ch[u][1]]+=add[u]; 80 sum[ch[u][0]]+=(LL)add[u]*size[ch[u][0]]; 81 sum[ch[u][1]]+=(LL)add[u]*size[ch[u][1]]; 82 add[u]=0; 83 } 84 } 85 86 void New_Node(int &u,int f,int c)//新建一个结点,f为父节点 87 { 88 u=++tot; 89 val[u]=sum[u]=c; 90 pre[u]=f; 91 size[u]=1; 92 ch[u][1]=ch[u][0]=add[u]=0; 93 } 94 95 void Build_Tree(int &u,int l,int r,int f)//建树,中间结点先建立,然后分别对区间两端在左右子树建立 96 { 97 if(l>r) 98 return; 99 int m=(l+r)>>1; 100 New_Node(u,f,a[m]); 101 if(l<m) 102 Build_Tree(ch[u][0],l,m-1,u); 103 if(r>m) 104 Build_Tree(ch[u][1],m+1,r,u); 105 Push_Up(u); 106 } 107 108 void Rotate(int x,int c)//旋转操作,c=0 表示左旋,c=1 表示右旋 109 { 110 int y=pre[x]; 111 Push_Down(y);// 先将Y结点的标记向下传递(因为Y在上面) 112 Push_Down(x);//再把X的标记向下传递 113 ch[y][!c]=ch[x][c];//类似SBT,要把其中一个分支先给父节点 114 pre[ch[x][c]]=y; 115 pre[x]=pre[y]; 116 if(pre[y])//如果父节点不是根结点,则要和父节点的父节点连接起来 117 { 118 ch[pre[x]][ch[pre[y]][1]==y]=x; 119 } 120 pre[y]=x; 121 ch[x][c]=y; 122 Push_Up(y); 123 } 124 125 void Splay(int x,int f)//Splay操作,把根结点x转到结点f的下面 126 { 127 Push_Down(x); 128 while(pre[x]!=f) 129 { 130 int y=pre[x]; 131 if(pre[y]==f)//父结点的父亲即为f,执行单旋转 132 Rotate(x,ch[y][0]==x); 133 else 134 { 135 int z=pre[y]; 136 int g=(ch[z][0]==y); 137 if(ch[y][g]==x) 138 Rotate(x,!g),Rotate(x,g);//之字形旋转 139 else Rotate(y,g),Rotate(x,g);//一字形旋转 140 } 141 } 142 Push_Up(x);// 最后再维护X结点 143 if(f==0)//更新根结点 144 { 145 root=x; 146 } 147 } 148 149 void Rotate_Under(int k,int f)//把第k位的数伸展到f下方 150 { 151 //找到处在中序遍历第k个结点,并将其旋转到结点f 的下面 152 int p=root;//从根结点开始 153 Push_Down(p);// 由于要访问p的子结点,将标记下传 154 while(size[ch[p][0]]!=k)//p的左子树的大小 155 { 156 if(k<size[ch[p][0]])// 第k个结点在p左边,向左走 157 { 158 p=ch[p][0]; 159 } 160 else//否则在右边,而且在右子树中,这个结点不再是第k个 161 { 162 k-=(size[ch[p][0]]+1); 163 p=ch[p][1]; 164 } 165 Push_Down(p); 166 } 167 Splay(p,f);//执行旋转 168 } 169 170 int Insert(int k)//插入结点 171 { 172 int r=root; 173 while(ch[r][key[r]<k]) 174 r=ch[r][key[r]<k]; 175 New_Node(ch[r][k>key[r]],r,k); 176 //将新插入的结点更新至根结点 177 //Push_Up(r); 178 Splay(ch[r][k>key[r]],0); 179 return 1; 180 } 181 182 int Get_Pre(int x)//找前驱,即左子树的最右结点 183 { 184 int tmp=ch[x][0]; 185 if(tmp==0) 186 return INF; 187 while(ch[tmp][1]) 188 { 189 tmp=ch[tmp][1]; 190 } 191 return key[x]-key[tmp]; 192 } 193 194 int Get_Next(int x)//找后继,即右子树的最左结点 195 { 196 int tmp=ch[x][1]; 197 if(tmp==0) 198 return INF; 199 while(ch[tmp][0]) 200 { 201 tmp=ch[tmp][0]; 202 } 203 return key[tmp]-key[x]; 204 } 205 206 LL Query(int l,int r)//查询[l,r]之间的和 207 { 208 Rotate_Under(l-1,0); 209 Rotate_Under(r+1,root); 210 return sum[Key_value]; 211 } 212 213 void Update(int l,int r)//更新 214 { 215 int k; 216 scanf("%d",&k); 217 Rotate_Under(l-1,0); 218 Rotate_Under(r+1,root); 219 add[Key_value]+=k; 220 sum[Key_value]+=size[Key_value]*k; 221 } 222 223 void Init()//初始化 224 { 225 for(int i=0; i<n; i++) 226 scanf("%d",&a[i]); 227 ch[0][0]=ch[0][1]=pre[0]=size[0]=0; 228 add[0]=sum[0]=0; 229 root=tot=0; 230 New_Node(root,0,-1); 231 New_Node(ch[root][1],root,-1); //头尾各加入一个空位 232 size[root]=2; 233 Build_Tree(Key_value,0,n-1,ch[root][1]); //让所有数据夹在两个-1之间 234 Push_Up(ch[root][1]); 235 Push_Up(root); 236 } 237 238 int main() 239 { 240 while(~scanf("%d%d",&n,&q)) 241 { 242 Init(); 243 while(q--) 244 { 245 char op; 246 scanf(" %c",&op); 247 int x,y; 248 scanf("%d%d",&x,&y); 249 if(op==‘Q‘) 250 printf("%lld\n",Query(x,y)); 251 else 252 Update(x,y); 253 } 254 } 255 return 0; 256 }
标签:
原文地址:http://www.cnblogs.com/wyj-jenny/p/5778655.html