splay

P1486 [NOI2004]郁闷的出纳员

一世执手 提交于 2019-11-30 04:27:44
题目链接 这道题需要动态插入,删除,求排名,看到这就想到了平衡树。由于本人只会splay,所以就用splay来做这道题,这道题插入和删除都是模板,但是题中还有一个比价坑的地方就是工资的调整。但我做不到在平衡树上修改点权。如果每一个都去插入和删除的复杂度显然非常高。看了题解发现,可以维护一个变量来记录每次工资调整时的值delta,像这样的话我们插入的k值就应该是k-delta,为什么要这样做呢,因为我们显然无法修改之前的点权,这样一来就是维护一个相对的大小。 重点还有如何把工资低于下界的人都删去。我们在操作之前提前插入两个哨兵节点+/-inf,这样可以避免操作时的错误,这是一个技巧,需要记住。在删除的时候把权值为-inf的点旋转到根节点,然后将全值为下界的点转到根节点的右儿子上,这是根节点右儿子的左儿子就是比工资下界小的所有节点,然后直接删除即可。 注意再求排名的时候要减去两个插入的哨兵节点。 代码如下: #include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; const int inf=0x7fffffff; int n,minn; char opt[maxn]; int key[maxn],fa[maxn],cnt[maxn],size[maxn],ch[maxn][2]; int rt,sz; int

【模板】LCT

寵の児 提交于 2019-11-30 02:16:49
\(LCT\) 是解决动态树问题的一种强力的数据结构,这种数据结构维护的是由若干 \(splay\) 节点构成的森林。 \(LCT\) 结构中采用了实链剖分的策略,即:将树边划分为实边和虚边,其中实边指的是 \(splay\) 节点通过节点中儿子指针相连的边,虚边指的是通过父节点指针相连的边。实边所构成的所有点在同一棵 \(splay\) 中,虚边连接不同的 \(splay\) 。这样,就将原树的边集划分到了若干 \(splay\) 中。 #include <bits/stdc++.h> using namespace std; struct node { node *l, *r, *p; int val, sum, rev; node(int _val) : val(_val), sum(_val) { l = r = p = NULL; rev = 0; } void unsafe_reverse() { swap(l, r); rev ^= 1; } void pull() { sum = val; if (l != NULL) { l->p = this; sum ^= l->sum; } if (r != NULL) { r->p = this; sum ^= r->sum; } } void push() { if (rev) { if (l != NULL) { l-

【题解】序列终结者

我的梦境 提交于 2019-11-29 12:20:58
题目链接 \(splay\) 维护区间加,区间翻转,区间 \(max\) . 维护标记,区间加标记和区间翻转标记。记住两个是同级的,都要更新。 更详细的解释请参考笔者的题解:数列。 #include<cstdio> #include<iostream> #include<cstring> using namespace std; const int MAXN=5e5+10; const int inf=2e9; int n,m,id,rt,vx[MAXN]; struct Splay_Tree{ int val,fa,maxn,ch[2]; int tag,siz,rev; }tr[MAXN]; inline void pushup(int x){ tr[x].maxn=max(tr[tr[x].ch[0]].maxn,max(tr[tr[x].ch[1]].maxn,tr[x].val)); tr[x].siz=tr[tr[x].ch[0]].siz+tr[tr[x].ch[1]].siz+1; } inline int build(int l,int r,int f){ if(l>r)return 0;int x=++id,mid=l+r>>1; tr[x].fa=f;tr[x].val=tr[x].maxn=vx[mid]; tr[x].ch[0]=build(l,mid-1

【模板】Splay

笑着哭i 提交于 2019-11-29 12:00:56
Splay应该是我目前写过的最长的代码了(188) #include <iostream> #include <cstdio> using namespace std; //Mystery_Sky //Splay #define M 1000100 #define INF 0x3f3f3f3f #define ll long long inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();} return x*f; } int fa[M], val[M], cnt[M], son[M][2], size[M]; int tot, root, n; inline void S_clear(int p) { son[p][0] = son[p][1] = fa[p] = size[p] = cnt[p] = val[p] = 0; } inline void update(int p) { if (p){ size[p] = cnt[p]; if (son[p][0]) size[p] += size[son[p][0]]; if (son[p]

LCT 维护边双 / 点双的模板

ⅰ亾dé卋堺 提交于 2019-11-29 10:34:05
用 \(\text{LCT}\) 维护边双的做法是:加入一条非树边时,将这段树上路径合并为一个点代表这个边双,具体实现用并查集合并点,在 \(\text{Splay}\) 与 \(\text{Access}\) 的过程中对辅助树上父亲做路径压缩。 用 \(\text{LCT}\) 维护点双的做法是:加入一条非树边时,将这段树上路径全部砍断,新建一个点代表这个点双,将原来那些点向新点连虚边。 实现方法:直接用 \(\text{Splay}\) 与 \(\text{Access}\) 提取路径并且 \(\text{DFS}\) 遍历这个路径就可以了。 代码是维护路径桥与割点的个数。 洛谷链接 #include<bits/stdc++.h> using namespace std; const int N = 200005; int n,q,lst; struct bcj { int f[N]; void init() { for(int i=1; i<=n; i++) f[i]=i; } int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } }G; namespace LCT { const int maxn = N; int ch[maxn][2],fa[maxn],s[maxn],rev[maxn]; #define lc(x)

QTREE5 Link-Cut Tree

青春壹個敷衍的年華 提交于 2019-11-29 09:39:01
\(Problem\ Link\) 题目大意 你被给定一棵n个点的树,点从1到n编号。每个点可能有两种颜色:黑或白。我们定义dist(a,b)为点a至点b路径上的边个数。 一开始所有的点都是黑色的。 要求作以下操作: 0 i 将点i的颜色反转(黑变白,白变黑) 1 v 询问dist(u,v)的最小值。u点必须为白色(u与v可以相同),显然如果v是白点,查询得到的值一定是0。 特别地,如果作'1'操作时树上没有白点,输出-1. 思想分析 这题的动态点分治做法看我的 总结 这题动态点分治做法还挺简单的,但是LCT做法就复杂一些, 不过总体来说还是板子 我们定义 \(lmn[x],rmn[x]\) 分别代表在 \(splay\) 中 \(x\) 的子树里深度最浅的点能够到达最近的白点的距离, 和深度最深的点能够到达最近的白点的距离 还要开个堆或者 \(multiset\) 维护一下子树中的 \(lmn\) 最小值. 这道题询问的是点到最近的白点的距离,所以就不需要维护子树信息了 不需要拉链,也就不用split,makert,findrt等一大堆操作了 这个时候LCT还是跑的很快的,比动态点分治快了一倍 \(qwq\) 代码实现 /* @Date : 2019-09-10 17:14:38 @Author : Adscn (adscn@qq.com) @Link : https://www

P4036 [JSOI2008]火星人(splay+hash+二分)

余生颓废 提交于 2019-11-29 03:26:00
P4036 [JSOI2008]火星人 Splay维护hash,查询二分 $a[x].vl=a[lc].vl*ha[a[rc].sz+1]+a[x].w*ha[a[rc].sz]+a[rc].vl$ #include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef unsigned long long ull; int read(){ char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+c-48,c=getchar(); return x; } #define N 300005 const int bas=13331; int n,Q; char q[N],q1[3]; ull ha[N]; struct node{int ch[2],sz,fa; ull vl,w;}a[N]; struct Splay{ #define lc a[x].ch[0] #define rc a[x].ch[1] #define mid (l+r)/2 int rt,u; inline void up(int x){ a[x].sz=a[lc].sz+a[rc].sz+1, a

伸展树(Splay Tree)

蓝咒 提交于 2019-11-28 22:28:27
一种二分查找树 大多数操作有O(logN)的时间复杂度,但有时很慢 // O(N) 可以快速访问最近访问过的元素(这个结构的核心就是缓存) 每次查找之后将已找到的元素旋到树根部,旋转情况分为Zig-zag(3弯)、Zig-zig(3直)、Zig(2) 来源: https://www.cnblogs.com/hanasaki/p/11430576.html

CF558E A Simple Task

巧了我就是萌 提交于 2019-11-28 21:52:27
CF558E A Simple Task WOC怎么又一个simple task? 操作就是区间排序+最终询问 第一反应是Splay(不对呀,我明明不会Splay的......😀) 后来看了看,感觉Splay不可做(我连Splay都不会,怎么就觉得不能做了) 感觉线段树比较靠谱 观察题目发现小写字母只有26个(常识) 往元素上去做文章 将排序用另外一种方式呈现 用桶排序的思想 开一个桶记录区间1~26字母出现的个数 然后区间赋值,达到排序目的 输出只要遍历一下线段树的叶子节点即可 代码: #include<bits/stdc++.h> using namespace std; const int N=100005; int n,m; int a[N]; int cal[30]; struct Sugment_Tree{ int LZT[N<<2]; int t[N<<2][30]; #define il inline #define mid (l+r)/2 Sugment_Tree(){memset(t,0,sizeof(t));memset(LZT,0,sizeof(LZT));} il void push_up(int num){ for(int i=1;i<=26;i++){t[num][i]=t[num<<1][i]+t[num<<1|1][i];} } il void

[洛谷P1501][国家集训队]Tree II(LCT)

送分小仙女□ 提交于 2019-11-28 16:13:43
题目链接 做了几道LCT,发现大多涉及到修改树上路径。本题也一样,4个操作中其实主要麻烦的就是加C和乘C,只需要维护区间和的同时记录加法和乘法的lazy标记,并且在pushdown的时候先乘再加即可。 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const unsigned int maxn = 120010; 5 const unsigned int mod = 51061; 6 unsigned int fa[maxn], ch[maxn][2], siz[maxn], val[maxn], sum[maxn], lr[maxn], lm[maxn], la[maxn], st[maxn];//父亲节点,左右儿子节点,当前子数节点个数,当前值,lr标记,辅助数组。 7 inline bool isroot(unsigned int x) {//判断x是否为所在splay的根 8 return ch[fa[x]][0] != x && ch[fa[x]][1] != x; 9 } 10 inline void pushup(unsigned int x) { 11 sum[x] = (sum[ch[x][0]] + sum[ch[x][1]] + val[x]) % mod;