splay

平衡树

橙三吉。 提交于 2019-11-28 13:26:47
例题1:[bzoj3224]&[luogu3369]普通平衡树(平衡树模板题)    题意: 维护一个集合,支持:1.加入x;2.删除x;3.查询x排名;4.查询排名x的数;5.查询x前驱;6.查询x后继   0.3查询(有多少个数小于x)+1,4查询存在x个数小于等于它的最小数,5查询排名为(x排名-1)的数,6查询排名为((x+1)排名)的数   1.可以开一棵权值线段树/trie树(动态开点)来维护,维护区间和即可,比较简单 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mid (0LL+l+r>>1) 4 #define N 10000005 5 int V,n,r,p,x,f[N],ls[N],rs[N]; 6 void update(int &k,int l,int r,int x,int y){ 7 if (!k)k=++V; 8 if (l==r){ 9 f[k]+=y; 10 return; 11 } 12 if (x<=mid)update(ls[k],l,mid,x,y); 13 else update(rs[k],mid+1,r,x,y); 14 f[k]=f[ls[k]]+f[rs[k]]; 15 } 16 int query1(int k,int l,int r,int x,int

[SHOI2014]三叉神经树——LCT

旧时模样 提交于 2019-11-28 08:39:28
题面:    LOJ#2187 解析:    显然修改一次需要修改一条到根的链, 维护链当然就想到用LCT了   结果就想偏了, 本来想分别维护虚子树信息与整棵子树信息,结果发现很难维护。然后去自学了一发   我们定义一个点的点权为它的儿子节点中选$1$的个数   考虑更改一个点的点权要么对它上方的链中连续的$1$或连续$2$, 因此Splay中每个节点分别维护能到的最深的不是$1$的点与不是$2$的点,然后是一个区间修改和单点修改了,直接在Splay中搞就行了   注意更新父亲节点时的顺序,先用右儿子更新,再用左儿子   代码: #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int maxn = 500004, inf = 0x3f3f3f3f; inline int read() { int ret, f = 1; char c; while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f = -1; ret=c-'0'; while((c=getchar())&&(c>='0'&&c<='9')) ret = (ret<<3)+(ret<<1)+c-'0'; return ret*f; } int n, m, f[maxn

[Splay]luogu P2596 书架

僤鯓⒐⒋嵵緔 提交于 2019-11-27 23:30:35
https://www.luogu.org/problem/P2596 分析 ?我怎么又开始写水题了 拿一个rev数组记录编号为s的书对应Splay中哪个节点 然后top就是将对应点旋至根,将左儿子嫁接到其后继上 bottom类似 insert操作只需要直接交换节点与其前驱/后继的信息 以及rev值 ask将s旋到根输出左儿子大小即可 query直接Get_Kth #include <iostream> #include <cstdio> using namespace std; const int N=8e4+10; struct Node { int c[2],sz,id,f; }t[N]; int rt,cnt; int n,m,rev[N]; void Update(int x) {t[x].sz=t[t[x].c[0]].sz+t[t[x].c[1]].sz+1;} bool Witch(int x) {return t[t[x].f].c[1]==x;} void Rotate(int x) { int f=t[x].f,gf=t[f].f,lr=Witch(x); t[x].f=gf;if (gf) t[gf].c[Witch(f)]=x; t[f].c[lr]=t[x].c[lr^1];if (t[x].c[lr^1]) t[t[x].c[lr^1]].f=f; t

[ZJOI2006]书架

[亡魂溺海] 提交于 2019-11-27 22:20:28
题目描述 有n本书从上到下放,有5种操作: 1.将编号为s的书放在最上面 2.把编号为s的书放在最下面 3.把编号为s的书上下移动一个位置或者不动 4.询问编号为s的书上面的书的个数 5.从上面数第k个书的编号 100%的数据,n,m <= 80000 题解 序列的平衡树。前后插入最值方便些。下标为编号 1.在原序列上删除,再插入在第一个数之后。 2.在原序列上删除,再插入在第n个数(因为自己出来了,最开始又有最值)之后。 3.不动就不管。他在原序列的位置为pos,先删除。向上移动的话插入在第pos-2个数之后;向下移动插入在第pos个数之后(自己出来了)。 4.把s旋到根,输出左儿子的size-1(最值) 5.查询第k+1即可 需要注意的就是-1是向上(可能只有我搞错,蠢哭);求编号s的位置,不是直接左儿子的size+1(因为不在根,再次蠢哭); #include<bits/stdc++.h> using namespace std; const int maxn=80005; int n,m,root; int a[maxn]; struct Splay{ int fa,size,s[2]; }tr[maxn]; template<class T>inline void read(T &x){ x=0;int f=0;char ch=getchar(); while(

[NOI2004]郁闷的出纳员

家住魔仙堡 提交于 2019-11-27 22:18:25
题目描述 有一个公司,有四种操作: 1.拉一个人进入公司,有初始工资 2.每个人的工资加上x 3.每个人的工资减去x 4.查询第k大的工资 当一个人工资低于m时他就会立即离开公司。 要求对于询问输出答案,最后输出离开的人的个数。 题解 查询第k大就用平衡树搞。 主要要解决的就是如何高效的帮助人离开,可以想到找到第一个满足留在公司的人,他前面的人都不满足删掉即可。 2.3操作肯定是懒惰标记搞,因为是每个人平等的(同加同减),所以不会搞乱相对大小,在访问这个点的时候信息是更新好了的。用ret记录第一个满足的人,如果这个人的工资满足,ret就是这个人,然后往左儿子走,不然走右儿子。(可能有点绕,需要理解) 如果ret没记录说明全部都要走(好惨),这时特判搞一搞就好。记录了就旋到根,去掉根的左儿子。 有些地方要注意边界,因为splay可能没元素。 因为工资可能一样,所以要多记录一个num。 在在splay里面走的时候要push_down!!!!插入,删除,查k大 一个人初始工资不满足离开不加入最后答案(坑死我)。 #include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,m,root,cnt,ans; struct Splay{ int fa,s[2],size,tag,val,num; }tr

【NOI2005】维护数列

≡放荡痞女 提交于 2019-11-27 18:48:13
Description 维护一个数列的如下操作: 在某个位置后插入一段数列 在某个位置后删除一段数列 将连续的一段数列修改为某个值 将一段数列翻转 求一段数列的和 求整个数列的最大子段和 Solution 魔鬼Splay题 用Splay维护序列,以下标为权值建树。 如果插入一段序列,我们就将这段序列本身先建成一棵树,然后连到原来的树上即可 如果删除一段序列,那么直接切断子树即可 修改和翻转操作可以维护两个标记 最大子段和可以维护三个变量来实现 具体见代码 由于本题的空间限制比较严格,所以我们需要尽可能的节省空间,具体地,每次删除操作的时候,我们记录被删除的点的编号,放入一个“垃圾箱” 如果新建节点时垃圾箱不为空那么直接使用垃圾箱里的节点编号即可 时间复杂度为$O(mlogn)$ Code 1 #include <bits/stdc++.h> 2 namespace shl { 3 typedef long long ll; 4 using std :: max; 5 const int N = 500010; 6 const ll INF = 2147483647; 7 inline int read() { 8 int ret = 0, op = 1; 9 char c = getchar(); 10 while (!isdigit(c)) { 11 if (c == '-')

NOI2007货币兑换

核能气质少年 提交于 2019-11-27 18:23:40
用Splay维护凸包 以后还是建议用CDQ吧。 Splay比较难调试 /* @Date : 2019-08-18 08:04:31 @Author : Adscn (adscn@qq.com) @Link : https://www.cnblogs.com/LLCSBlog */ #include<bits/stdc++.h> using namespace std; #define IL inline #define RG register #define gi getint() #define gc getchar() #define File(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout) IL int getint() { RG int xi=0; RG char ch=gc; bool f=0; while(ch<'0'||ch>'9')ch=='-'?f=1:f,ch=gc; while(ch>='0'&&ch<='9')xi=(xi<<1)+(xi<<3)+ch-48,ch=gc; return f?-xi:xi; } template<typename T> IL void pi(T k,char ch=0) { if(k<0)k=-k,putchar('-'); if(k>=10)pi(k/10,0

[BeiJing 2017 Wc]神秘物质——Splay

被刻印的时光 ゝ 提交于 2019-11-27 14:09:01
题面    Bzoj4864 解析    合并序列相邻的两点,在序列中插入一个点,直接考虑Splay   先考虑如何完成操作3, 4   对于操作3,显然是区间最大减去区间最小,Splay维护一下即可   对于操作4,实际上就是求区间内相邻两数的差的绝对值的最小值(注意是绝对值,我一开始就写错了), 那么每个节点还需要维护当前点与它前驱的差的绝对值,查询$[l, r]$的答案,实际上是在Splay的$[l+1, r]$节点中查询   操作1:把x旋转至根,把x+2旋转至根的右儿子,x+1就是x+2的左儿子,直接删除。因为相邻两数的差也变了,所以先更新x+2的信息,再更新x的信息   操作2:把x旋转至根,x+1就是它的后继,在后继的左儿子新开一个节点,存信息,相邻两数的差改变了,所以后继的信息也要更新,再把新开的节点旋转至根,完成整颗Splay的信息更新   代码: #include<cstdio> #include<cmath> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn = 100004; template<class T> void read(T &re) { re=0; T sign=1; char tmp; while((tmp

Bzoj3786: 星系探索——Splay

纵饮孤独 提交于 2019-11-27 14:08:05
题面    Bzoj3786 解析    上课讲稿上的例题   这道题是套路题,是括号序的应用,进入节点时打上$+1$标记, 退出时打上$-1$标记,这个是作为点权的系数   先看操作2, 需要更改父节点,就是把一段区间提取出来,插入另一个地方,显然可以用Splay维护,先提取区间,再把新父亲的$+1$点旋转至根,把区间挂在根的后继的左儿子上,再把这个节点旋转至根,以更新信息   对于操作1,求点到根的路径和,就是求括号序列的前缀和,该点对应的$+1$点或$-1$点的前缀和都可,我是把$-1$的点旋转至根,答案就是根的左儿子的子树和,因此需要维护子树和   最后还有操作3,显然打标记,问题在对于子树和的改变, 记该子树中$+1$标记有$cnt1$个,$-1$标记有$cnt_1$个,则子树和需要加$(cnt1 - cnt_1) * delta$,因此需要维护子树中$+1$与$-1$的个数   代码: #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int maxn = 100004; template<class T> void read(T &re) {

学习——我用Splay打排序

孤人 提交于 2019-11-27 13:30:52
Splay 学习——我用 Splay 打排序 Description 给出N个数,你的任务就是把这n个数从小到大排序^_^ Input 第一行一个整数,N(1<=n<=200000),表示有N个数 接下来有N行,每行一个数 Output 输出N行,表示已经排序的N个数 Sample Input 5 2 3 1 4 5 Sample Output 1 2 3 4 5 这一题相信大家都会用排序算法,之所以我用 Splay, 是因为OJ上找不到 splay 的版题。。。 也因此,我只能尝试插入、Splay操作 代码 //请自行忽略这两行注释 //#pragma GCC optimize(2) //#pragma G++ optimize(2) #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; int a[200010]; struct node{ int son[2],v,t,f,size; node(){t=f=son[0]=son[1]=0;size=1;} }; node no[200010];int cntn=1,root=1; int getson(int x){return (int)(no[no[x].f].son[1]==x);} void updata(int x) {