数据结构之splay树
https://www.bilibili.com/video/av19879546
https://blog.csdn.net/u014634338/article/details/42465089
kuangbin
https://blog.csdn.net/changtao381/article/details/8936765
类别:二叉排序树
空间效率:O(n)
时间效率:O(logn)内完成插入查找,删除操作" role="presentation" style="position: relative;">O(logn)内完成插入查找,删除操作O(logn)内完成插入查找,删除操作
优点:每次查询会调整树的结构,使被查询平率高的条目靠近树的根。
关键操作是Splay
1、A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K Total Submissions:
128343 Accepted: 39841 Case Time Limit: 2000MS
Description
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000. The
second line contains N numbers, the initial values of A1, A2, … ,
AN. -1000000000 ≤ Ai ≤ 1000000000. Each of the next Q lines represents
an operation. “C a b c” means adding c to each of Aa, Aa+1, … , Ab.
-10000 ≤ c ≤ 10000. “Q a b” means querying the sum of Aa, Aa+1, … , Ab.
Output
You need to answer all Q commands in order. One answer in a line.
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
解题代码:
/*
* POJ 3468 A Simple Problem with Integers
* 经典的线段树题目,用splay tree来作为入门题
* 成段更新+区间求和
* 题目给定了n个数A1,A2,...An,有以下两种操作
* C a b c:把c加入到Aa,Aa+1,..Ab中
* Q a b:查询Aa,Aa+1,..Ab的和
* 需要的变量:pre,ch,size(这三个基本都要),key(保存结点的值),sum(子树值和),add(增量的标记)
* (一般标记类,正确的做法都是要先更新掉该点,标记是标记没有更新子结点)
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define Key_value ch[ch[root][1]][0]
const int MAXN=100010;
int pre[MAXN],ch[MAXN][2],size[MAXN],root,tot1;//父结点、左右孩子、子树规模、根结点、结点数量
int key[MAXN];//该点的值
int add[MAXN];//增量的延迟标记
long long sum[MAXN];//子树的和
int s[MAXN],tot2;//内存池、内存池容量(这题用不到,如果有删除操作,内存不够可以这样
int a[MAXN];//初始的数组,建树时候用
int n,q;
//debug部分
void Treavel(int x)
{
if(x)
{
Treavel(ch[x][0]);
printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size=%2d,key=%2d add=%2d sum=%I64d\n",x,ch[x][0],ch[x][1],pre[x],size[x],key[x],add[x],sum[x]);
Treavel(ch[x][1]);
}
}
void debug()
{
printf("root:%d\n",root);
Treavel(root);
}
//以上是debug
void NewNode(int &r,int father,int k)//一个是调用的时候注意变量顺序,还有r必须引用&
{
if(tot2)r=s[tot2--];//取得时候是tot2--,那么存的时候就要是++tot2
else r=++tot1;
pre[r]=father;
size[r]=1;//这个不能忘记 ,一定是1,否则可能出错
key[r]=k;
add[r]=0;
sum[r]=0;
ch[r][0]=ch[r][1]=0;
}
//给r为根的子树增加值,一定把当前结点的全部更新掉,再加个延迟标记表示儿子结点没有更新
void Update_Add(int r,int ADD)
{
if(r==0)return;
add[r]+=ADD;
key[r]+=ADD;
sum[r]+=(long long)ADD*size[r];
}
//通过孩子结点更新父亲结点
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]]+key[r];
}
//将延迟标记更新到孩子结点
void Push_Down(int r)
{
if(add[r])
{
Update_Add(ch[r][0],add[r]);
Update_Add(ch[r][1],add[r]);
add[r]=0;
}
}
//建树
//先建立中间结点,再两端的方法
void Build(int &x,int l,int r,int father)
{
if(l>r)return;
int mid=(l+r)/2;
NewNode(x,father,a[mid]);
Build(ch[x][0],l,mid-1,x);
Build(ch[x][1],mid+1,r,x);
Push_Up(x);
}
//初始化,前后各加一个king结点
void Init()
{
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
root=tot1=tot2=0;
ch[root][0]=ch[root][1]=pre[root]=size[root]=add[root]=sum[root]=0;
key[root]=0;
NewNode(root,0,-1);
NewNode(ch[root][1],root,-1);//头尾各加入一个空位
Build(Key_value,1,n,ch[root][1]);
Push_Up(ch[root][1]);
Push_Up(root);
}
//旋转,0为左旋,1为右旋 该部分基本固定
void Rotate(int x,int kind)
{
int y=pre[x];
Push_Down(y);
Push_Down(x);//先把y的标记向下传递,再把x的标记往下传递
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);//维护y结点
}
//Splay调整,将结点r调整到goal下面
void Splay(int r,int goal)
{
Push_Down(r);
while(pre[r]!=goal)
{
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个结点
int Get_Kth(int r,int k)
{
Push_Down(r);
int t=size[ch[r][0]]+1;
if(t==k)return r;
if(t>k)return Get_Kth(ch[r][0],k);
else return Get_Kth(ch[r][1],k-t);
}
int Get_Min(int r)
{
Push_Down(r);
while(ch[r][0])
{
r=ch[r][0];
Push_Down(r);
}
return r;
}
int Get_Max(int r)
{
Push_Down(r);
while(ch[r][1])
{
r=ch[r][1];
Push_Down(r);
}
return r;
}
//区间增加一个值
//注意因为在前面增加了个结点,所以把第l个结点旋转到根结点,第r+2个结点旋转到根结点的右孩子,
//那么Key_value(ch[ch[root][1]][0]刚好就是区间[l,r]
void ADD(int l,int r,int D)
{
Splay(Get_Kth(root,l),0);//第l个点到根结点
Splay(Get_Kth(root,r+2),root);//第r+2个点到根结点的右孩子
Update_Add(Key_value,D);
Push_Up(ch[root][1]);
Push_Up(root);
}
//查询区间的和
long long Query_Sum(int l,int r)
{
Splay(Get_Kth(root,l),0);//第l个点到根结点
Splay(Get_Kth(root,r+2),root);//第r+2个点到根结点的右孩子
return sum[Key_value];
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(scanf("%d%d",&n,&q)==2)
{
Init();//这个不能忘记
while(q--)
{
char op[20];
int x,y,z;
scanf("%s",op);
if(op[0]=='Q')
{
scanf("%d%d",&x,&y);
printf("%I64d\n",Query_Sum(x,y));
}
else
{
scanf("%d%d%d",&x,&y,&z);
ADD(x,y,z);
}
}
}
return 0;
}
数据结构之splay树的更多相关文章
- Splay树分析
简述 Splay树是一种二叉查找平衡树,其又名伸展树,缘由是对其进行任意操作,树的内部结构都会发生类似伸张的动作,换言之,其读和写操作都会修改树的结构.Splay树拥有和其它二叉查找平衡树一致的读写时 ...
- Splay树-Codevs 1296 营业额统计
Codevs 1296 营业额统计 题目描述 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司 ...
- D&F学数据结构系列——B树(B-树和B+树)介绍
B树 定义:一棵B树T是具有如下性质的有根树: 1)每个节点X有以下域: a)n[x],当前存储在X节点中的关键字数, b)n[x]个关键字本身,以非降序存放,因此key1[x]<=key2[x ...
- ZOJ3765 Lights Splay树
非常裸的一棵Splay树,需要询问的是区间gcd,但是区间上每个数分成了两种状态,做的时候分别存在val[2]的数组里就好.区间gcd的时候基本上不支持区间的操作了吧..不然你一个区间里加一个数gcd ...
- Splay树再学习
队友最近可能在学Splay,然后让我敲下HDU1754的题,其实是很裸的一个线段树,不过用下Splay也无妨,他说他双旋超时,单旋过了,所以我就敲来看下.但是之前写的那个Splay越发的觉得不能看,所 ...
- 暑假学习日记:Splay树
从昨天开始我就想学这个伸展树了,今天花了一个上午2个多小时加下午2个多小时,学习了一下伸展树(Splay树),学习的时候主要是看别人博客啦~发现下面这个博客挺不错的http://zakir.is-pr ...
- 1439. Battle with You-Know-Who(splay树)
1439 路漫漫其修远兮~ 手抄一枚splay树 长长的模版.. 关于spaly树的讲解 网上很多随手贴一篇 貌似这题可以用什么bst啦 堆啦 平衡树啦 等等 这些本质都是有共同点的 查找.删除特 ...
- 伸展树(Splay树)的简要操作
伸展树(splay树),是二叉排序树的一种.[两个月之前写过,今天突然想写个博客...] 伸展树和一般的二叉排序树不同的是,在每次执行完插入.查询.删除等操作后,都会自动平衡这棵树.(说是自动,也就是 ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
随机推荐
- Callable线程
写一个Callable线程 import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; im ...
- C# 读自己的资源文件
Assembly assm = this.GetType().Assembly;//Assembly.LoadFrom(程序集路径); foreach (string resName in assm. ...
- 学习swift从青铜到王者之swift枚举07
空枚举 //空枚举 enum SomeEnumeration { // enumeration definition goes here } 枚举基本类型 //枚举基本类型 enum CompassP ...
- POJ2573 Bridge 经典的过桥问题
曾经遇到过类似的.纪念一下!这题同一时候也是 ZOJ1877.经典的过桥问题 是有个博客解说的非常好的 戳这里 挺久曾经.遇到过一个基本一样的,那个题目仅仅要求求出 最短时间就可以,如今还有过桥的过 ...
- Hadoop-08-Hive本地独立式安装
1.安装mysql sudo apt-get install mysql-server mysql-client 2.使用root账户登录mysql数据库,新建存放hive元数据的数据库.如果叫hiv ...
- JMeter 系列之—-01使用
用Jmeter 做压测,总体与LoadRunner 类似: 一.线程组 1. 线程数 2. 循环次数 单个线程循环次数 3. Ramp-up Period(in seconds) [1]决定多长时间启 ...
- js常用的正则表达操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 下载Youku视频观看
所需工具: 1.chorme内核浏览器,如Chorme.Firefox等等 2.vlc视频播放器 准备工作完成,开始工作 1.打开优酷的随便一个视频 2.按下F12选择Network(网络)选择Med ...
- Vi/Vim查找替换使用方法【转】
原文地址:http://wzgyantai.blogbus.com/logs/28117977.html vi/vim 中可以使用 :s 命令来替换字符串.该命令有很多种不同细节使用方法,可以实现复杂 ...
- 队列,管道,manager模块
###生产者消费者关系### 主要是解耦(高内聚,低耦合),借助队列来实现生产者消费者 模型 栈:先进后出(First In Last Out 简称:FILO) 队列:先进先出(First In Fi ...