bzoj1588,1208,1503
进入splay tree的学习中;
据说splay tree在理论上功能十分强大,好好学;
splay首先一定是一棵BST,所以记不得的时候画个图就明白;
首先总结一下splay基本的操作左旋,右旋;
设节点x,其父节点y
左旋:保留x的右子树,y的左子树,将y插入到x的x的左子树上并原来x的左子树接到y的右子树上,右旋反之;
而Splay(x,s) (将x伸展为s的孩子)要分三种情况:
若y是根节点,则x在哪儿往相反方向旋(左孩子右旋,右孩子左旋)
若y的父节点是z,则若三点一线,先旋y再旋x
否则一直旋x,在哪儿反向旋;具体画个图就知道了;
核心就是在哪儿反向旋(是左孩子右旋,右孩子左旋)
然后分析一下splay的几个操作;
插入:很简单,按着BST的做法既可,之后把插入的元素伸展到根节点
删除:分类讨论,若要删除的节点x无孩子,那么直接删除(这不废话吗);
若x有1个孩子,那么把孩子接到父节点y上,然后删除;
若x有2个孩子,那么,就有点麻烦了;
首先找到x的后继p(第一个比x大的数),用其代替x,而本来p的孩子(只可能是有孩子),接到p父节点的左子树上
找K大数,这个跟BST一样
这三道平衡树的题目中,1503比较有思维难度(毕竟是noi)
有两个问题比较难处理 1.怎么修改 2.怎么删除
删除比较好办,把limit插入到tree中,然后删去左子树即可(注意:这里相等的值要插入到左子树中,原因见后文)
UPD:实际上相等的值处理应该在对应节点记录下出现次数即可
修改难住了我,一开始想的是想线段树一样加一个lazy域,可觉得删除,伸展时还要维护实在太烦;
后经人点醒,才明白完全可以只用一个全局变量temp来维护,
temp表示整个序列(不只是当前,当然好像也不好表示当前……)都修改的值,于是
相应的新加入的值就变为x-temp
附代码:
var lazy,a,fa,count:array[..] of longint;
son:array[..,..] of longint;
temp,m,n,lim,i,x,root,s:longint;
c:char; procedure middle(i:longint);
begin
if son[i,]<> then middle(son[i,]);
write(a[i]+temp,' ');
if son[i,]<> then middle(son[i,]);
end; procedure pushup(x:longint); //一种很简便的维护子树方法,不需要记得这么烦
begin
count[x]:=count[son[x,]]+count[son[x,]]+;
end; procedure rotate(x,f:longint);
var y,p:longint;
begin
y:=fa[x];
fa[x]:=fa[y];
if fa[y]<> then
begin
if son[fa[y],]=y then son[fa[y],]:=x
else son[fa[y],]:=x;
end;
son[y,-f]:=son[x,f];
if son[x,f]<> then fa[son[x,f]]:=y;
fa[y]:=x;
son[x,f]:=y;
pushup(y); //注意顺序
pushup(x);
end; procedure splay(x:longint);
var y:longint;
begin
while fa[x]<> do
begin
y:=fa[x];
if fa[y]= then
begin
if son[y,]=x then
rotate(x,)
else rotate(x,);
end
else if son[fa[y],]=y then
begin
if son[y,]=x then
begin
rotate(y,);
rotate(x,);
end
else begin
rotate(x,);
rotate(x,);
end;
end
else if son[fa[y],]=y then
begin
if son[y,]=x then
begin
rotate(x,);
rotate(x,);
end
else begin
rotate(y,);
rotate(x,);
end;
end;
end;
pushup(x);
root:=x;
end; procedure insert(x:longint);
var p:longint;
begin
inc(m);
son[m,]:=;
son[m,]:=;
count[m]:=;
a[m]:=x;
if root= then
begin
root:=m;
fa[m]:=;
end
else begin
p:=root;
repeat
inc(count[p]);
if a[p]>=x then //注意:为什么要把等于的插入左子树,因为删除的时候删的是左子树,而如果比现在插入到右子树,根据之前的分析,将x旋转到根后,相等的父节点p会变成x的左子树(在哪就被旋转到相反到,相等时不需要删去)
begin
if son[p,]= then break;
p:=son[p,];
end
else begin
if son[p,]= then break;
p:=son[p,];
end;
until false;
fa[m]:=p;
if a[p]>=x then son[p,]:=m else son[p,]:=m;
splay(m);
end;
end; function kth(x:longint):longint;
var p,h:longint;
begin
p:=root;
h:=x;
while h<>count[son[p,]]+ do
begin
if h>count[son[p,]]+ then
begin
h:=h-count[son[p,]]-;
p:=son[p,];
end
else p:=son[p,];
end;
exit(p);
end; begin
readln(n,lim);
temp:=;
for i:= to n do
begin
readln(c,x);
if c='I' then
begin
if x>=lim then
begin
insert(x-temp);
s:=s+;
end;
end
else if c='S' then
begin
temp:=temp-x;
insert(lim-temp);
if son[m,]<> then
begin
root:=son[m,];
fa[son[m,]]:=;
m:=m-;
end
else begin
root:=;
fillchar(fa,sizeof(fa),);
fillchar(count,sizeof(count),);
fillchar(son,sizeof(son),);
m:=;
end;
end
else if c='A' then temp:=temp+x
else if c='F' then
if x>count[root] then writeln(-) else writeln(a[kth(x)]+temp); //注意打印的时候不忘+temp
// middle(root);
end;
writeln(s-count[root]); //count[root]就代表了整棵树的规模,即现有的人数
end.
bzoj1503
另外附删除节点编号为x的子程序;
function find(i,f:longint):longint;
var p:longint;
begin
p:=son[i,f];
while son[p,-f]<> do p:=son[p,-f]; //后缀:右孩子的最左节点
find:=p;
end; procedure delete(i:longint);
var p,q:longint;
begin
dec(t);
if t= then
begin
root:=;
f:=-;
exit;
end;
p:=;
if son[fa[i],]=i then p:=
else if son[fa[i],]=i then p:=;
if (son[i,]<>) and (son[i,]<>) then
begin
q:=find(i,);
if root=i then root:=q;
if fa[q]<>i then //当后继就是x的左孩子时,后继的孩子依然接在后继上
begin
son[fa[q],]:=son[q,];
fa[son[q,]]:=fa[q];
end;
fa[q]:=fa[i];
son[fa[i],p]:=q;
son[q,]:=son[i,];
fa[son[q,]]:=q;
if son[i,]<>q then //小细节
begin
son[q,]:=son[i,];
fa[son[q,]]:=q;
end;
fa[i]:=-;
son[i,]:=;
son[i,]:=;
end
else begin
q:=;
if son[i,]<> then q:=;
if son[i,]<> then q:=;
if root=i then root:=son[i,q];
if q= then son[fa[i],p]:=
else begin
fa[son[i,q]]:=fa[i];
son[fa[i],p]:=son[i,q];
end;
fa[i]:=-;
end;
end;
UPD:这是比较糟糕的写法……
bzoj1588,1208,1503的更多相关文章
- BZOJ 1503: [NOI2004]郁闷的出纳员
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 10526 Solved: 3685[Submit][Stat ...
- hdu 1503 Advanced Fruits
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1503 思路:这是一道最长公共子序列的题目,当然还需要记录路径.把两个字符串的最长公共字串记录下来,在递 ...
- BZOJ 1208: [HNOI2004]宠物收养所
1208: [HNOI2004]宠物收养所 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 7514 Solved: 2982[Submit][Sta ...
- 【bzoj1588】 HNOI2002—营业额统计
http://www.lydsy.com/JudgeOnline/problem.php?id=1588 (题目链接) 题意 给出一个序列,对于每一个数,找出之前与它相差最小的数,两者相减取绝对值加入 ...
- CSU 1503 点到圆弧的距离(2014湖南省程序设计竞赛A题)
题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1503 解题报告:分两种情况就可以了,第一种是那个点跟圆心的连线在那段扇形的圆弧范围内,这 ...
- HDU 1503 带回朔路径的最长公共子串
http://acm.hdu.edu.cn/showproblem.php?pid=1503 这道题又WA了好几次 在裸最长公共子串基础上加了回溯功能,就是给三种状态各做一个 不同的标记.dp[n][ ...
- POJ 1503
http://poj.org/problem?id=1503 对于这个题我也是醉了,因为最开始是有学长和我们说过这个题目的,我以为我记得题目是什么意思,也就没看题目,结果按案例去理解题意,结果WA了一 ...
- BZOJ 1503: [NOI2004]郁闷的出纳员 splay
1503: [NOI2004]郁闷的出纳员 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作 ...
- csuoj 1503: 点到圆弧的距离
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1503 1503: 点到圆弧的距离 时间限制: 1 Sec 内存限制: 128 MB Speci ...
随机推荐
- PHP基础入门教程 PHP循环函数
PHP循环主要有四种:while,do…while,for,foreach.下面我们分开讲解每种循环的用法. while语句: 只要指定的条件成立,则循环执行代码块. 格式: while(expr) ...
- 关于B/S系统中文件上传的大小限制怎么做
1.前端:采用flash控件或者Html5的特性(有浏览器版本要求)来判断文件大小.纯html或js是没法判断用户上传文件大小的. 2.nginx:服务器端的第一道防线,一般会有对上传文件做大小限制. ...
- AngularJS快速开始
Hello World! 开始学习AngularJS的一个好方法是创建经典应用程序“Hello World!”: 使用您喜爱的文本编辑器,创建一个HTML文件,例如:helloworld.html. ...
- Oracle的rownum原理
Oracle中,按特定条件查询前N条记录,用个rownum就搞定了: SQL> select * from dept where rownum<3; 而对rownum用"> ...
- ActiveMQ中Session设置的相关理解
名词解释: P:生产者 C:消费者 服务端:P 或者 ActiveMQ服务 客户端:ActiveMQ服务 或者 C 客户端成功接收一条消息的标志是这条消息被签收.成功接收一条消息一般包括如下三个阶段: ...
- easy ui easyui-linkbutton 禁用、启用
<a id="btn_update_Shop" name="btn_update_Shop" class="easyui-linkbutton& ...
- Xcode免证书打包ipa
1,创建证书 打开“钥匙串访问”创建证书 填写好内容后点击继续,之后的步骤什么都不用改,一路点击“确定”和“继续”,最后完成这个向导就可以了. 我们创建的证书是不被信任的,右键点击证书选择“显示简介” ...
- Ubuntu 设置root用户登录
由于 Ubuntu 是基于 Debian 的 linux 操作系统,在默认的情况下,是没有超级用户(superuser, root)的,但有些系统操作必须有超级用户的权限才能进行,如手动释放内存等. ...
- ORA-01034:oracle不可用 的解决方法
晚上打开SQLPlus输入用户名和密码老是登不了,出现如上一行代码 "ORA-01034:ORACLE不可用"的错误 解决思路: 1.检查服务是否启动, 2.如果已经启动,说明数据 ...
- TWaver初学实战——如何在EasyUI中插入TWaver
TWaver是一款强大的图形界面开发组件,可以很方便地集成到其他开发工具中.今天就用一个小例子来操练如何结合TWaver和EasyUI进行网页开发. 准备工作 俗话说他山之玉可以直接拿来,EasyUI ...