前言
我不想调试!!!
就是这样,本文诞生了
线段树
update函数
- 没有在update函数里面pushdown和pushup
- update的时候lazy标记是累加的而非赋值:
void update(int l,int r,int nl,int nr,int rt,ll w){
if(l<=nl&&nr<=r){
lazy[rt]+=w;//不能写成lazy[rt]=w;
sum[rt]+=(nr-nl+1)*w;
return;
}
pushdown(nl,nr,rt);
int mid=(nl+nr)>>1;
if(mid>=l)update(l,r,nl,mid,rt<<1,w);
if(mid<r)update(l,r,mid+1,nr,rt<<1|1,w);
pushup(rt);
}
数组大小
- 数组没有开够,记得要开4倍大小
精度问题
-
一般来讲数据都会特别鬼畜啊,记得开longlong
-
特别注意有没有精度转化,即int相乘后面转longlong时可能在中间爆掉:
sum[rt]+=(long long)num*(nr-nl+1);//一定要转化精度
splay
1.关于插入极大和极小值这个技巧
一定要插入足够大和小的值,否则会出一些玄学错误,一定要能多大就多大
比如这个情况
2.关于rotate函数的不同写法
rotate函数理论上来说有不同的写法,但是原则就是一定要把那四个点的父子关系和相关信息维护正确
比如:在维护x的父子关系时,要确定y是x的哪个儿子,理论上可以通过:
1.原来x相对于y的位置关系
2.x的被影响的那个子节点相对于x的位置关系
两种方式来判断
这时问题来了,如果x没有子节点呢,这时x的子节点就是0(空都是0),那么这样就没有办法正确的更新父子关系,所以以后还是用前一种方法吧
3.关于insert函数
A
if(_p)tree[_p].son[_w>tree[_p].w]=_cur;
这一句特别容易被打掉!!!!!
如果不打的话,新插入的这个点的父节点就没法和这个点相连了
B
int _cur=root,_p=0;
while(_cur&&tree[_cur].w!=_w)
{
_p=_cur;
_cur=tree[_cur].son[_w>tree[_cur].w];
}
在寻找值为w的点时,while循环里面的判断条件是当前所在的点不为空!
这和find函数中的即将到达的点不为空需要区别
4.关于找第k大函数
while(1){
if(tree[Lson(cur)].siz>=k)
cur=Lson(cur);
else if(tree[cur].cnt+tree[Lson(cur)].siz>=k){
Splay(cur);
return tree[cur].w;
}
else if(tree[cur].cnt+tree[Lson(cur)].siz<k){
k-=tree[cur].cnt+tree[Lson(cur)].siz;//
cur=Rson(cur);
}
}
注意第3个else if里面的两句话的顺序不可以换!
虽然很显然,但是在没有高度专注的情况下总是会出现这些神奇的情况
5.关于删点函数
记清楚步骤:先旋前驱到root,再旋后继到其前驱的下面
不能把后继旋到根上去了
6.关于找排名函数
在大部分定义里面,数m的排名为小于m的数的个数+1,不能忘记+1!
7.关于函数的返回值
有些函数返回值可以是某个点的值,也可以是某个点的编号
但是如果返回值,就不可以得到编号,若返回编号,却可以得到值
如果不做强行规定,在构建代码的时候会出现RE的风险
因此,规定如下:
- _pos:返回_x的位置关系:0/1
- update:void
- rotate:void
- splay:void
- insert:void
- find:void/int(int=>返回编号,不可返回值!)
- findkth:int(返回编号)
- pre:int(返回编号)
- bac:int(返回编号)
- del:void
- getrank:int=>返回某个数的排名
8.关于宏的使用
由于结构体的特点,用结构体方式构建的Splay必然会显得比较冗杂,语句偏长容易积累bug且不容易调试,因此推荐以下宏定义:
#define f(x) tree[x].fa
#define Lson(x) tree[x].son[0]
#define Rson(x) tree[x].son[1]
#define ADX_son(x,w) tree[x].son[tree[x].w<w]
//按大小的儿子~~