嘟嘟嘟




突然觉得splay挺有意思,唯一不足的是这几天是一天一道,debug到崩溃。




做了几道平衡树基础题后,对这题有莫名的自信,还算愉快的敲完了代码后,发现样例都过不去,然后就陷入了无限的debug环节了……算了,伤心的事就别再提了。




说一下这题怎么做:

1.插入

不说了

void insert(int x)
{
int now = root, f = 0;
while(now && t[now].val != x) f = now, now = t[now].ch[x > t[now].val];
if(now) t[now].cnt++;
else
{
now = ++ncnt;
if(f) t[f].ch[x > t[f].val] = now;
t[now].fa = f;
t[now].ch[0] = t[now].ch[1] = 0;
t[now].siz = t[now].cnt = 1; t[now].val = x;
}
splay(now, 0);
}

2.删除

找\(x\)的前驱\(a\),后继\(b\),把\(a\)旋到根,再把\(b\)旋到\(a\)的右儿子,这样\(b\)的左子树就只剩\(x\)唯一一个节点了。若有多个,\(cnt--\),否则清零。

然后一定要把\(b\)旋转到根。刚开始我很不理解,多亏了学姐说:不旋转怎么更新平衡树啊!我才知道旋转还有一个作用,就是更新这个节点的所有祖先的值。跟线段树回溯的时候更新祖先节点一样。

我刚开始写了一个\(clear\)函数,然后因为没传实参debug了半天……

void del(int x)
{
int a = pre(x), b = nxt(x);
splay(a, 0); splay(b, a);
int now = t[b].ch[0];
if(t[now].cnt > 1) t[now].cnt--, splay(now, 0);
else t[b].ch[0] = 0;
}

3.查询\(x\)数的排名(数据保证\(x\)存在)

有一个很棒的做法是查找\(x\)并把\(x\)旋到根,然后返回根的左子树的大小就行。

然而我刚开始是用\(bst\)的思路写的:如果\(x\)小于当前节点权值,到左子树找;如果等于,返回累加值\(ret\)加当前节点大小;否则,\(ret\)加上左子树和当前节点大小,然后去右子树找。

这个思路是没问题的,写起来也不难,然而我被某谷的第\(12\)个点卡掉了:数据是这样的:先添加\(50000\)个点,接下来\(50000\)个操作全是查找\(i\)或\(50000\)的排名。按我的写法是虽然是稳定的\(O(n \log{n})\),但就是TLE;而换了我刚开始说的那个写法后,由于询问可能是\(O(1)\)可能是\(O(n \log{n})\)的,竟然迅速的AC了……

注释掉的是\(bst\)的写法

int queryKth(int x)
{
find(x);
return t[t[root].ch[0]].siz;
/*int now = root, ret = 0;
while(1)
{
if(t[now].val > x) now = t[now].ch[0];
else if(t[now].val == x) return ret + t[t[now].ch[0]].siz;
else ret += t[t[now].ch[0]].siz + t[now].cnt, now = t[now].ch[1];
}*/
}

4.查询排名为\(x\)的数

按\(bst\)的写法就行:如果\(x\)小于等于左子树大小,去左子树找;否则如果小于等于左子树加上当前节点的大小,返回当前节点权值;否则\(x\)减去左子树和节点大小,到右子树去找。

int queryX(int k)
{
int now = root;
while(1)
{
if(k <= t[t[now].ch[0]].siz) now = t[now].ch[0];
else if(k <= t[t[now].ch[0]].siz + t[now].cnt) return t[now].val;
else k -= (t[t[now].ch[0]].siz + t[now].cnt), now = t[now].ch[1];
}
}

5,6.前驱,后继

不说了

void find(int x)
{
int now = root;
if(!now) return;
while(t[now].val != x && t[now].ch[x > t[now].val]) now = t[now].ch[x > t[now].val];
splay(now, 0);
}
int pre(int x)
{
find(x);
//_PrintTr(root);
if(t[root].val < x) return root;
int now = t[root].ch[0];
while(t[now].ch[1]) now = t[now].ch[1];
return now;
}
int nxt(int x)
{
find(x);
//_PrintTr(root);
if(t[root].val > x) return root;
int now = t[root].ch[1];
while(t[now].ch[0]) now = t[now].ch[0];
return now;
}



最后当然要放完整代码啦。(自认为挺短的)
```c++
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) {last = ch; ch = getchar();}
while(isdigit(ch)) {ans = (ans = 10) write(x / 10);
putchar(x % 10 + '0');
}

int n;

struct Tree

{

int ch[2], fa;

int siz, cnt, val;

}t[maxn];

int root, ncnt = 0;

void _PrintTr(int now)

{

if(!now) return;

printf("nd:%d val:%d ls:%d rs:%d\n", now, t[now].val, t[t[now].ch[0]].val, t[t[now].ch[1]].val);

_PrintTr(t[now].ch[0]); _PrintTr(t[now].ch[1]);

}

void pushup(int now)

{

t[now].siz = t[t[now].ch[0]].siz + t[t[now].ch[1]].siz + t[now].cnt;

}

void rotate(int x)

{

int y = t[x].fa, z = t[y].fa, k = (t[y].ch[1] == x);

t[z].ch[t[z].ch[1] == y] = x; t[x].fa = z;

t[y].ch[k] = t[x].ch[k ^ 1]; t[t[x].ch[k ^ 1]].fa = y;

t[x].ch[k ^ 1] = y; t[y].fa = x;

pushup(y); pushup(x);

}

void splay(int x, int s)

{

while(t[x].fa != s)

{

int y = t[x].fa, z = t[y].fa;

if(z != s)

{

if((t[z].ch[1] == y) ^ (t[y].ch[1] == x)) rotate(x);

else rotate(y);

}

rotate(x);

}

if(!s) root = x;

}

void insert(int x)

{

int now = root, f = 0;

while(now && t[now].val != x) f = now, now = t[now].ch[x > t[now].val];

if(now) t[now].cnt++;

else

{

now = ++ncnt;

if(f) t[f].ch[x > t[f].val] = now;

t[now].fa = f;

t[now].ch[0] = t[now].ch[1] = 0;

t[now].siz = t[now].cnt = 1; t[now].val = x;

}

splay(now, 0);

}

void find(int x)

{

int now = root;

if(!now) return;

while(t[now].val != x && t[now].ch[x > t[now].val]) now = t[now].ch[x > t[now].val];

splay(now, 0);

}

int pre(int x)

{

find(x);

//_PrintTr(root);

if(t[root].val < x) return root;

int now = t[root].ch[0];

while(t[now].ch[1]) now = t[now].ch[1];

return now;

}

int nxt(int x)

{

find(x);

//_PrintTr(root);

if(t[root].val > x) return root;

int now = t[root].ch[1];

while(t[now].ch[0]) now = t[now].ch[0];

return now;

}

void del(int x)

{

int a = pre(x), b = nxt(x);

splay(a, 0); splay(b, a);

int now = t[b].ch[0];

if(t[now].cnt > 1) t[now].cnt--, splay(now, 0);

else t[b].ch[0] = 0;

}

int queryKth(int x)

{

find(x);

return t[t[root].ch[0]].siz;

/int now = root, ret = 0;

while(1)

{

if(t[now].val > x) now = t[now].ch[0];

else if(t[now].val == x) return ret + t[t[now].ch[0]].siz;

else ret += t[t[now].ch[0]].siz + t[now].cnt, now = t[now].ch[1];

}
/

}

int queryX(int k)

{

int now = root;

while(1)

{

if(k <= t[t[now].ch[0]].siz) now = t[now].ch[0];

else if(k <= t[t[now].ch[0]].siz + t[now].cnt) return t[now].val;

else k -= (t[t[now].ch[0]].siz + t[now].cnt), now = t[now].ch[1];

}

}

int main()

{

//freopen("test.in", "r", stdin);

//freopen("ha.out", "w", stdout);

insert(-INF); insert(INF);

n = read();

for(int i = 1; i <= n; ++i)

{

int op = read(), x = read();

if(op == 1) insert(x);

else if(op == 2) del(x);

else if(op == 3) write(queryKth(x)), enter;

else if(op == 4) write(queryX(x + 1)), enter;

else if(op == 5) write(t[pre(x)].val), enter;

else write(t[nxt(x)].val), enter;

//_PrintTr(root);

}

return 0;

}

luogu P3369 【模板】普通平衡树(splay)的更多相关文章

  1. luoguP3391[模板]文艺平衡树(Splay) 题解

    链接一下题目:luoguP3391[模板]文艺平衡树(Splay) 平衡树解析 这里的Splay维护的显然不再是权值排序 现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶...) 那么,继续 ...

  2. 【洛谷P3369】普通平衡树——Splay学习笔记(一)

    二叉搜索树(二叉排序树) 概念:一棵树,若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉搜索树 ...

  3. 洛谷.3369.[模板]普通平衡树(Splay)

    题目链接 第一次写(2017.11.7): #include<cstdio> #include<cctype> using namespace std; const int N ...

  4. 洛谷.3391.[模板]文艺平衡树(Splay)

    题目链接 //注意建树 #include<cstdio> #include<algorithm> const int N=1e5+5; //using std::swap; i ...

  5. 数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline _ ...

  6. [luogu P3369]【模板】普通平衡树(Treap/SBT)

    [luogu P3369][模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删 ...

  7. 替罪羊树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 闲的没事,把各种平衡树都写写 比较比较... 下面是替罪羊树 #include <cstdio> #inc ...

  8. 红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

    二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) 喜闻乐见拿到了luo ...

  9. 【阶梯报告】洛谷P3391【模板】文艺平衡树 splay

    [阶梯报告]洛谷P3391[模板]文艺平衡树 splay 题目链接在这里[链接](https://www.luogu.org/problemnew/show/P3391)最近在学习splay,终于做对 ...

  10. luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

    luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include< ...

随机推荐

  1. Unix 高手的10个好习惯

    引言 当您经常使用某个系统时,往往会陷入某种固定的使用模式.有时,您没有养成以尽可能最好的方式做事的习惯.有时,您的不良习惯甚至会导致出现混乱.纠正此类缺点的最佳方法之一,就是有意识地采用抵制这些坏习 ...

  2. 用hmac验证客户端的合法性

    服务器端程序 import os import hmac import socket def auth(conn): secret_key = b'rock' rand_b = os.urandom( ...

  3. vmware虚拟机挂载Windows磁盘的两种方法

    第一种 vmware虚拟机通过ntfs-3g挂接windows盘 1.共享windows盘虚拟机设置——>添加硬盘——>选择IDE——>使用物理磁盘——>选择本地盘(单分区)— ...

  4. 【日常记录】【unity3d】 2D跳跃过快导致角色某帧陷入地面

    如果角色运动过快会导致嵌入地面再反弹出来 : 可以使用更高质量的检测方式 "Continuous" :就可以解决这个问题

  5. 自己搭建anki服务器

    目录 centos端 电脑客户端 安卓端 centos端 # 安装服务 yum -y install python-setuptools easy_install Ankiserver mkdir - ...

  6. machine learning model(algorithm model) .vs. statistical model

    https://www.analyticsvidhya.com/blog/2015/07/difference-machine-learning-statistical-modeling/ http: ...

  7. Oracle EBS 新增OAFM个数

    在 $INST_TOP/ora/10.1.3/opmn/conf/opmn.xml中找到<process-type id="oafm" module-id="OC4 ...

  8. ffemp语音转码

    分享一款windows上很不错的  程序员专业转码软件 ffemp 首先先下载ffemp转码软件 https://pan.baidu.com/s/10BoahyWJlI9e-_rB_yCiLA 下载之 ...

  9. Celery学习---Celery 分布式队列介绍及安装

    Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery, ...

  10. Oracle数据库运维:要对监听日志文件(listener.log)进行定期清理,如果不定期清理,会遇到下面一些麻烦

    原文链接: http://www.lookdaima.com/WebForms/WebPages/Blanks/Pm/Docs/DocItemDetail.aspx?EmPreviewTypeV=2& ...