[您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
1500: [NOI2005]维修数列
Time Limit: 10 Sec Memory Limit: 64 MB
Description

Input
输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。
Output
对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。
Sample Input
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
Sample Output
10
1
10
HINT

这次我们学习无旋treap的区间操作(如果没有了解过无旋treap,你可以选择查看我上一篇讲解博文[您有新的未分配科技点]无旋treap:从好奇到入门)
这道例题,真的是平衡树神题……
区间操作我们并没有见过,但我们可以联想一下单点操作时普通平衡树里都干了些什么:
"合并两棵树”和“把一棵树的前k个节点分裂出来”
可不可以用这些操作进行区间操作?显然是可以的,只要我们每次新建一棵树就可以了
在具体操作之前,我们先来看一下区间操作的原理:为什么平衡树的区间操作是对的?
我们知道,平衡树是一种二叉排序树,它的各种操作的前提是不会改变排序树的中序遍历
如果我们按照想要的中序遍历建树并且合并,我们一定能得到正确的区间,所以平衡树的区间操作是正确的。
这也是平衡树的两大建立方式之一:按中序遍历建树(另外一种是按权值建树……就是最常见的那种)
这样就涉及到一个没有出现的函数:build(建树)函数
build函数的原理和"笛卡尔树"的建造是比较像的(没有听说过笛卡尔树?请看笛卡尔树)
我们用下面一张图来简要讲解一下build的构造过程(注意,定义key为维护堆性质的随机键值)

假设这是我的无旋treap目前的状态,我用一个栈来维护这棵树最右边的一条链,并且每一次在右下角处插入节点
假设我此时我们在9的右儿子添加了一个13,若13的key值小于栈顶元素9的key,那么就开始退栈,停止退栈的条件有两个,满足任意一个即停止:

Treap *stack[N],*x,*last;
inline Treap *build()
{
int p=;
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
x=newTreap(a[i]);last=null;
while(p&&stack[p]->key > x->key)
{stack[p]->update();last=stack[p];stack[p--]=null;}
if(p)stack[p]->ch[]=x;
x->ch[]=last;stack[++p]=x;
}
while(p)stack[p--]->update();
return stack[];
}
有了这个build操作,在加上之前的操作,我们很容易得出
insert=split+build+merge+merge
delete=split+split+merge
下面在考虑其他4个操作:
make-same:和线段树一样打标记并且下传就好
注意,这个操作会影响到最大连续和的求解,需要更新维护信息(更新谁请读者思考)。
reserve:这也是平衡树区间操作的经典考题……区间翻转问题
对于节点o,我们也是用一个标记来维护这个问题,
操作到这个节点时下传标记,然后用swap交换o的左右儿子即可。
不幸的是,这个操作也会影响到最大连续和的求解,也需要更新维护信息(更新谁请读者思考)。
get-sum:每个节点多维护一个sum附加值,在维护节点信息的时候更新即可。
get-max:这个操作也是一种经典的询问。
它需要我们多维护下面三个信息:最大前缀和,最大后缀和以及最大连续和。
对于每一个节点的最大前缀和,这个值可能是
1°它的左儿子的前缀和;
2°左儿子的和+它的val
3°左儿子的和+它的val+右儿子的前缀和
最大后缀和同理。
而最大连续和,这个范围可能完全在左儿子中,可能完全在右儿子中,也可能卡在左右儿子中间
所以我们可能的取值为:
1°左儿子的最大连续和;
2°右儿子的最大连续和
3°max(0,左儿子的后缀和)+它的val+max(右儿子的前缀和)
这样我们就解决了这个问题。觉得还是不太明白的同学可以去做一下cogs775山海经这道题,它就是单纯询问最大连续和一个操作。
这样,6个操作都被我们处理完了。本题不卡内存和时间,所以放心大胆的码就可以了……
代码见下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
const int N=;
const int MAXN=;
const int inf=0x3f3f3f3f;
int n,a[N],point;
struct Treap
{
Treap *ch[];
int val,key,size,sum,l,r,m;bool flip,mark;
Treap(){val=l=r=m=-inf;sum=;size=;mark=flip=;key=rand();}
inline void update()
{
size=ch[]->size+ch[]->size+;
sum=val+ch[]->sum+ch[]->sum;
l=max(ch[]->l,max(ch[]->sum+val,ch[]->sum+val+ch[]->l));
r=max(ch[]->r,max(val+ch[]->sum,ch[]->r+val+ch[]->sum));
m=max(ch[]->m,max(max(,ch[]->r)+val+max(,ch[]->l),ch[]->m));
}
}*null=new Treap(),*root=null,*stack[N],*x,*last;
typedef pair<Treap*,Treap*> D;
inline void Maintain_flip(Treap *o)
{
if(o==null)return;
o->flip^=;swap(o->l,o->r);
}
inline void Maintain_mark(Treap *o,int c)
{
if(o==null)return;
o->val=c;o->sum=o->size*c;
o->l=o->r=o->m=max(o->size*c,c);
o->mark=;
}
inline void pushdown(Treap *o)
{
if(o==null)return;
if(o->flip)
{
o->flip^=;
Maintain_flip(o->ch[]);
Maintain_flip(o->ch[]);
swap(o->ch[],o->ch[]);
}
if(o->mark)
{
Maintain_mark(o->ch[],o->val);
Maintain_mark(o->ch[],o->val);
o->mark=;
}
}
inline Treap* newTreap(int val)
{
Treap *o=new Treap();
o->ch[]=o->ch[]=null;o->key=rand();
o->val=o->sum=val;o->size=;o->flip=o->mark=;
o->m=o->l=o->r=val;
return o;
}
Treap *merge(Treap *a,Treap *b)
{
if(a==null)return b;
if(b==null)return a;
pushdown(a);pushdown(b);
if(a->key < b->key)
{a->ch[]=merge(a->ch[],b);a->update();return a;}
else
{b->ch[]=merge(a,b->ch[]);b->update();return b;}
}
D split(Treap *o,int k)
{
if(o==null) return D(null,null);
D y;pushdown(o);
if(o->ch[]->size>=k)
{y=split(o->ch[],k);o->ch[]=y.second;o->update();y.second=o;}
else
{y=split(o->ch[],k-o->ch[]->size-);o->ch[]=y.first;o->update();y.first=o;}
return y;
}
inline Treap *build()
{
int p=;
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
x=newTreap(a[i]);last=null;
while(p&&stack[p]->key > x->key)
{stack[p]->update();last=stack[p];stack[p--]=null;}
if(p)stack[p]->ch[]=x;
x->ch[]=last;stack[++p]=x;
}
while(p)stack[p--]->update();
return stack[];
}
void adjust(Treap *o)
{
if(o==null)return;
if(o->ch[]!=null)adjust(o->ch[]);
if(o->ch[]!=null)adjust(o->ch[]);
delete o;
}
inline void insert()
{
int pos;scanf("%d%d",&pos,&n);
Treap *o=build();
D x=split(root,pos);
root=merge(merge(x.first,o),x.second);
}
inline void erase()
{
int pos;scanf("%d%d",&pos,&n);
D x=split(root,pos-);
D y=split(x.second,n);
adjust(y.first);
root=merge(x.first,y.second);
}
inline void reverse()
{
int pos;scanf("%d%d",&pos,&n);
D x=split(root,pos-);
D y=split(x.second,n);
Maintain_flip(y.first);
root=merge(merge(x.first,y.first),y.second);
}
inline void make_same()
{
int pos,c;scanf("%d%d%d",&pos,&n,&c);
D x=split(root,pos-);
D y=split(x.second,n);
Maintain_mark(y.first,c);
root=merge(merge(x.first,y.first),y.second);
}
inline int get_sum()
{
int pos;scanf("%d%d",&pos,&n);
if(n==)return ;
D x=split(root,pos-);
D y=split(x.second,n);
int ret=y.first->sum;
root=merge(merge(x.first,y.first),y.second);
return ret;
}
int main()
{
int m;scanf("%d%d",&n,&m);root=build();
char opt[];
while(m--)
{
scanf("%s",opt);
switch(opt[])
{
case 'I':{insert();break;}
case 'D':{erase();break;}
case 'M':
{
if(opt[]=='K'){make_same();break;}
else {printf("%d\n",root->m);break;}
}
case 'G':{printf("%d\n",get_sum());break;}
case 'R':{reverse();break;}
}
}
}
BZOJ1500
[您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )的更多相关文章
- [您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我 ...
- [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec Mem ...
- [您有新的未分配科技点]博弈论进阶:似乎不那么恐惧了…… (SJ定理,简单的基础模型)
这次,我们来继续学习博弈论的知识.今天我们会学习更多的基础模型,以及SJ定理的应用. 首先,我们来看博弈论在DAG上的应用.首先来看一个小例子:在一个有向无环图中,有一个棋子从某一个点开始一直向它的出 ...
- [您有新的未分配科技点]博弈论入门:被博弈论支配的恐惧(Nim游戏,SG函数)
今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们. 博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略.(其实这 ...
- [您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解
这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见) 先简单介绍一下0-1Trie:一个0-1Trie节点只有 ...
- [您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)
只会统计数位个数或者某种”符合简单规律”的数并不够……我们需要更多的套路和应用 数位dp中常用的思想是“分类讨论”思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 ...
- [您有新的未分配科技点]数位dp:从懵X到板子(例题:HDU2089 不要62)
数位dp主要用来处理一系列需要数数的问题,一般套路为“求[l,r]区间内满足要求的数/数位的个数” 要求五花八门……比如“不出现某个数字序列”,“某种数的出现次数”等等…… 面对这种数数题,暴力的想法 ...
- [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树
这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰 ...
- [BZOJ3223]文艺平衡树 无旋Treap
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...
随机推荐
- UTC时间转为正常日期
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);SimpleDa ...
- WCF中操作的分界于调用顺序和会话的释放
操作分界 在WCF操作契约的设计中,有时会有一些调用顺序的业务,有的操作不能最先调用,有的操作必须最后调用,比如在从一个箱子里拿出一件东西的时候,必须先要执行打开箱子的操作,而关上箱子的操作应该在一切 ...
- Maven学习(十八)-----Maven依赖管理
其中一个Maven的核心特征是依赖管理.管理依赖关系变得困难的任务一旦我们处理多模块项目(包含数百个模块/子项目). Maven提供了一个高程度的控制来管理这样的场景. 传递依赖发现 这是很通常情况下 ...
- python根据正则表达式的简单爬虫
今天根据正则表达式简单的爬了一下大众点评,把北京的美食爬了爬,(店铺名,人均消费,地址) import re import urllib.request from urllib.request imp ...
- 设计模式C++实现
准备写一系列笔记用来记录学习设计模式的过程,同时写出自己对几种主要的设计模式的理解,以及编码实现,同时总结. 主要参考书籍就是 <Head First Design Patterns>这本 ...
- mysql innodb myisam 比较
InnoDB: 支持事务处理等 不加锁读取 支持外键 支持行锁 不支持FULLTEXT类型的索引 不保存表的具体行数,扫描表来计算有多少行 DELETE 表时,是一行一行的删除 InnoDB 把数据和 ...
- python中取整的几种方法
#encoding:utf-8import math #向上取整print "math.ceil---"print "math.ceil(2.3) => " ...
- Planning The Expedition(暴力枚举+map迭代器)
Description Natasha is planning an expedition to Mars for nn people. One of the important tasks is t ...
- 20135313_exp4
实验四 GUI界面的设计和运用 20135313吴子怡 一.实验目的 结合项目,为每个密码学算法设计能够提供使用者用户体验的操作界面,实现加解密.求得消息摘要的功能. 二.代码举例(备注:其 ...
- 20135313_exp5
课程:Java程序与设计 班级:1353 姓 名:吴子怡 学号:20135313 小组成员: 20135113肖昱 成绩: 指导教师:娄嘉鹏 实验日期:2 ...