博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[NOI2005]维护数列——Splay
阅读量:5282 次
发布时间:2019-06-14

本文共 6608 字,大约阅读时间需要 22 分钟。

题面

  

解析

    这道题集齐了大部分Splay的经典操作,上课时wys老师讲了一下,这个题还是很值得一做的

  基操:

    Splay上的所有区间操作都需要提取区间,如果要提取一个[l, r]的区间,只需要把l-1的点转到根,再把r+1的点转到根的右儿子,那么根的右儿子的左儿子的整颗子树就是我们要提取的区间

  操作1:插入

    这个题的插入并非插入一个数,而是插入一个数列,一个一个的插入肯定会T的,所以就把插入的数列先建成一棵树,再提取区间,这时根的右儿子的左儿子为空,只需要把新树的根挂在根的右儿子的左儿子上,最后更新信息即可

  操作2:删除

    还是先提取区间,断开根的右儿子与它的左儿子相连的边就行,但这道题的空间有限,需要回收节点编号,最好开个栈来存编号,用队列的话有点浪费空间,而且必须开很大的数组才能在CodeVS上AC,在洛谷上好像区别不大,当然我说的队列是手写队列,你用STL我也没话说

  操作3:修改

    同样是先提取区间,把根的右儿子的左儿子修改后打上标记即可

  操作4:翻转

    提取区间,把根的右儿子的左儿子修改后打上标记

  操作5:求和

    提取区间,输出

  操作6:求和最大的子序列的和

    终于不用提取区间了,直接输出就行。但维护信息的时候很麻烦,与线段树求和最大的子列类似,我们需要维护区间的子序列最大值,以及左右区间的最大子序列的和,在向上更新的时候,需要像下面这样操作, 其中ls是x的左儿子,rs是右儿子,mx是和最大子序列的和,lx是左区间最大子序列的和,rx是右区间最大子序列的和

  细节:

       0号节点的mx要赋负无穷,因为有些点没有左儿子或右儿子,避免更新答案时出错(大导演wys这个细节调了1个小时)

    标记下传的时候要注意有没有左儿子或右儿子,不然会改变0号节点的信息,我搞了半个小时

    伸展操作(splay)不要写挂了,我搞了1个小时

  代码:

 

#include
using namespace std;const int maxn = 500005, inf = 0x3f3f3f3f;template
void read(T &re){ re=0; T sign=1; char tmp; while((tmp=getchar())&&(tmp<'0'||tmp>'9')) if(tmp=='-') sign=-1; re=tmp-'0'; while((tmp=getchar())&&(tmp>='0'&&tmp<='9')) re=(re<<3)+(re<<1)+(tmp-'0'); re*=sign;}int n, m, root, id[maxn], tot;int a[maxn];struct splay_tree{ int s[2], siz, fa, tag; int val, mx, lx, rx, sum, c; void init(int x) { val = sum = mx = x; lx = rx = max(x, 0); siz = 1; c = inf; } void Clear() { s[0] = s[1] = siz = fa = tag = 0; val = lx = rx = sum = mx = 0; c = inf; }}tr[maxn];int stak[maxn], top;void update(int x){ int ls = tr[x].s[0], rs = tr[x].s[1]; tr[x].sum = tr[ls].sum + tr[rs].sum + tr[x].val; tr[x].siz = tr[ls].siz + tr[rs].siz + 1; tr[x].mx = max(tr[ls].mx, tr[rs].mx); tr[x].mx = max(tr[x].mx, tr[ls].rx + tr[rs].lx + tr[x].val); tr[x].lx = max(tr[ls].lx, tr[ls].sum + tr[x].val + tr[rs].lx); tr[x].rx = max(tr[rs].rx, tr[rs].sum + tr[x].val + tr[ls].rx);}void spread(int x){ int ls = tr[x].s[0], rs = tr[x].s[1]; if(tr[x].c != inf) { if(ls) { tr[ls].val = tr[ls].c = tr[x].c; tr[ls].sum = tr[ls].siz * tr[x].c; tr[ls].lx = tr[ls].rx = max(tr[ls].sum, 0); tr[ls].mx = max(tr[ls].val, tr[ls].sum); } if(rs) { tr[rs].val = tr[rs].c = tr[x].c; tr[rs].sum = tr[rs].siz * tr[x].c; tr[rs].lx = tr[rs].rx = max(tr[rs].sum, 0); tr[rs].mx = max(tr[rs].val, tr[rs].sum); } tr[x].c = inf; tr[x].tag = 0; } if(tr[x].tag) { if(ls) { tr[ls].tag ^= 1; swap(tr[ls].lx, tr[ls].rx); swap(tr[ls].s[0], tr[ls].s[1]); } if(rs) { tr[rs].tag ^= 1; swap(tr[rs].lx, tr[rs].rx); swap(tr[rs].s[0], tr[rs].s[1]); } tr[x].tag = 0; }}void Rotate(int x){ int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x), w = (tr[z].s[1] == y), son = tr[x].s[k^1]; tr[y].s[k] = son;tr[son].fa = y; tr[x].s[k^1] = y;tr[y].fa = x; tr[z].s[w] = x;tr[x].fa = z; update(y);update(x);}void Splay(int x, int to){ while(tr[x].fa != to) { int y = tr[x].fa, z = tr[y].fa; if(z == to) { Rotate(x); break; } Rotate((tr[y].s[1] == x) != (tr[z].s[1] == y) ? x: y); Rotate(x); } if(!to) root = x;}int Find(int x){ int now = root; while(1) { spread(now); int ls = tr[now].s[0], rs = tr[now].s[1]; if(x == tr[ls].siz + 1) return now; if(x <= tr[ls].siz) now = ls; else x -= tr[ls].siz + 1, now = rs; }}void build(int l, int r, int f){ int mid = (l + r)>>1, now = id[mid], pre = id[f]; if(l == r) tr[now].init(a[mid]); if(l < mid) build(l, mid - 1, mid); if(mid < r) build(mid + 1, r, mid); tr[now].val = a[mid]; tr[now].fa = pre; tr[now].c = inf; update(now); if(pre) tr[pre].s[mid >= f] = now;}void Insert(int pos, int len){ for( int i = 1; i <= len; ++i) read(a[i]); for( int i = 1; i <= len; ++i) if(top) id[i] = stak[top--]; else id[i] = ++tot; build(1, len, 0); int x = id[(1+len)>>1]; int y = Find(pos + 1), z = Find(pos + 2); Splay(y, 0);Splay(z, y); tr[x].fa = z; tr[z].s[0] = x; update(z);update(y);}void Recycle( int x){ if(!x) return ; int ls = tr[x].s[0], rs = tr[x].s[1]; stak[++top] = x; tr[x].Clear(); if(ls) Recycle(ls); if(rs) Recycle(rs);}void Del(int pos, int len){ int x = Find(pos), y = Find(pos + len + 1); Splay(x, 0);Splay(y, x); Recycle(tr[y].s[0]); tr[y].s[0] = 0; update(y);update(x);}void Change(int pos, int len, int val){ int x = Find(pos), y = Find(pos + len + 1); Splay(x, 0);Splay(y, x); int z = tr[y].s[0]; tr[z].val = tr[z].c = val; tr[z].sum = tr[z].siz * val; tr[z].lx = tr[z].rx = max(tr[z].sum, 0); tr[z].mx = max(tr[z].val, tr[z].sum); update(y);update(x);}void Rever(int pos, int len){ int x = Find(pos), y = Find(pos + len + 1); Splay(x, 0);Splay(y, x); int z = tr[y].s[0]; if(tr[z].c == inf) { tr[z].tag ^= 1; swap(tr[z].lx, tr[z].rx); swap(tr[z].s[0], tr[z].s[1]); update(y);update(x); }}int Query(int pos, int len){ int x = Find(pos), y = Find(pos + len + 1); Splay(x, 0);Splay(y, x); int z = tr[y].s[0]; return tr[z].sum;}int main(){ read(n);read(m); tr[0].mx = a[1] = a[n+2] = -inf; for( int i = 1; i <= n; ++i) read(a[i+1]); for( int i = 1; i <= n + 2; ++i) id[i] = i; build(1, n + 2, 0); root = (n + 3)>>1; tot = n + 2; tr[0].s[1] = root; for( int i = 1; i <= m; ++i) { char opt[10]; int pos, len; scanf("%s", opt); if(opt[0] == 'I') { read(pos);read(len); Insert(pos, len); } else if(opt[0] == 'D') { read(pos);read(len); Del(pos, len); } else if(opt[0] == 'M' && opt[2] == 'K') { int val; read(pos);read(len);read(val); Change(pos, len, val); } else if(opt[0] == 'R') { read(pos);read(len); Rever(pos, len); } else if(opt[0] == 'G') { read(pos);read(len); printf("%d\n", Query(pos, len)); } else printf("%d\n", tr[root].mx); } return 0;}
View Code

 

转载于:https://www.cnblogs.com/Joker-Yza/p/11224430.html

你可能感兴趣的文章
关于不断刷新界面jsp+ajax
查看>>
课程总结
查看>>
gcc/g++ 如何支持c11 / c++11标准编译
查看>>
js高阶函数应用—函数防抖和节流
查看>>
牛客 545A 小A与最大子段和 & CF 660F Bear and Bowling 4
查看>>
eclipse 中java/scala 混合的maven项目 工作环境篇
查看>>
顺序栈与两栈共享空间-C语言实现
查看>>
【mongo】可以用localhost启动,无法用ip启动问题的解决
查看>>
【QT】视频播放
查看>>
HTML中使用javascript解除禁止input输入框代码:
查看>>
揭开Redis的神秘面纱
查看>>
Object流
查看>>
bzoj1293 [SCOI2009]生日礼物
查看>>
转 10 个 Nginx 的安全提示
查看>>
Windows Phone开发(8):关于导航的小技巧 转:http://blog.csdn.net/tcjiaan/article/details/7285062...
查看>>
React零碎知识点回顾
查看>>
字符串类型 字符串下标 字符串的方法 切片 for循环的一些总结
查看>>
Ajax学习笔记1之第一个Ajax应用程序
查看>>
数据库查询问题小记
查看>>
validate插件:验证密码没有空格 用户名是5-10位 至少包含数字和大小写字母中的两种字符...
查看>>