Luogu P2042 [NOI2005]维护数列(平衡树)
题意
题目描述
请写一个程序,要求维护一个数列,支持以下\(6\)种操作:(请注意,格式栏中的下划线‘_’表示实际输入文件中的空格)
输入输出格式
输入格式:
输入文件的第\(1\)行包含两个数\(N\)和\(M\),\(N\)表示初始时数列中数的个数,\(M\)表示要进行的操作数目。 第\(2\)行包含\(N\)个数字,描述初始时的数列。以下\(M\)行,每行一条命令,格式参见问题描述中的表格。
输出格式:
对于输入数据中的GET-SUM
和MAX-SUM
操作,向输出文件依次打印结果,每个答案(数字)占一行。
输入输出样例
输入样例#1:
9 8
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
输出样例#1:
-1
10
1
10
说明
你可以认为在任何时刻,数列中至少有\(1\)个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
\(50\%\)的数据中,任何时刻数列中最多含有\(30000\)个数;
\(100\%\)的数据中,任何时刻数列中最多含有\(500000\)个数。
\(100\%\)的数据中,任何时刻数列中任何一个数字均在\([-1000,1000]\)内。
\(100\%\)的数据中,\(M\leq 20000\),插入的数字总数不超过\(4000000\)。
思路
平衡树板子题(其实没有那么板),调试的难度还是很大的。
\(fhq\ Treap\)的线性建树是我从这题学到的新定西,直接放代码吧:
int build(int now)//新加入的点的数量
{
stack<int>S;//主要思想是维护一个单调栈
int last;
for(int i=1;i<=now;i++)
{
int x=new_node(a[i]);last=0;//新建结点
while(!S.empty()&&rnd(S.top())>rnd(x)) last=S.top(),update(S.top()),S.pop();//维护小根堆,寻找新结点的父节点
if(!S.empty()) rs(S.top())=x;
ls(x)=last,S.push(x);//把刚刚弹出的结点再接上
}
while(!S.empty()) last=S.top(),update(S.top()),S.pop();
return last;//返回新建树的根
}
本题的第二个难点是内存回收,可以直接开一个队列记录被删除的点,新建结点时从队列中取就好了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
int n,m,rt,cnt,a[MAXN];
bool H[MAXN];
int read()
{
bool f=true;int re=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=false;ch=getchar();}
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return f?re:-re;
}
struct Node
{
int ls,rs,sz,val,rnd,sum;
int sub_sum,left_sub_sum,right_sub_sum;
int change_tag;
bool reverse_tag;
#define ls(a) node[a].ls
#define rs(a) node[a].rs
#define sz(a) node[a].sz
#define val(a) node[a].val
#define rnd(a) node[a].rnd
#define sum(a) node[a].sum
#define lsm(a) node[a].left_sub_sum
#define rsm(a) node[a].right_sub_sum
#define sub(a) node[a].sub_sum
#define ctg(a) node[a].change_tag
#define rtg(a) node[a].reverse_tag
}node[MAXN];
queue<int>dustbin;
int new_node(int k)
{
int re;
if(!dustbin.empty()) re=dustbin.front(),H[dustbin.front()]=false,dustbin.pop();
else re=++cnt;
ls(re)=rs(re)=0,sz(re)=1;
val(re)=sum(re)=sub(re)=k;
lsm(re)=rsm(re)=max(k,0);
rnd(re)=rand();
ctg(re)=INT_MAX,rtg(re)=false;
return re;
}
void update(int now)
{
if(ls(now)&&rs(now))
{
sz(now)=sz(ls(now))+sz(rs(now))+1;
sum(now)=sum(ls(now))+sum(rs(now))+val(now);
lsm(now)=max(lsm(ls(now)),sum(ls(now))+val(now)+lsm(rs(now)));
rsm(now)=max(rsm(rs(now)),sum(rs(now))+val(now)+rsm(ls(now)));
sub(now)=max(max(sub(ls(now)),sub(rs(now))),rsm(ls(now))+val(now)+lsm(rs(now)));
}
else if(ls(now))
{
sz(now)=sz(ls(now))+1;
sum(now)=sum(ls(now))+val(now);
lsm(now)=max(max(lsm(ls(now)),sum(ls(now))+val(now)),0);
rsm(now)=max(0,val(now)+rsm(ls(now)));
sub(now)=max(sub(ls(now)),rsm(ls(now))+val(now));
}
else if(rs(now))
{
sz(now)=sz(rs(now))+1;
sum(now)=sum(rs(now))+val(now);
rsm(now)=max(max(rsm(rs(now)),sum(rs(now))+val(now)),0);
lsm(now)=max(0,val(now)+lsm(rs(now)));
sub(now)=max(sub(rs(now)),lsm(rs(now))+val(now));
}
else
{
sz(now)=1,sum(now)=sub(now)=val(now);
lsm(now)=rsm(now)=max(val(now),0);
}
}
void pushdown(int now)
{
if(rtg(now))
{
if(ls(now))
{
swap(ls(ls(now)),rs(ls(now)));
swap(lsm(ls(now)),rsm(ls(now)));
rtg(ls(now))^=1;
}
if(rs(now))
{
swap(ls(rs(now)),rs(rs(now)));
swap(lsm(rs(now)),rsm(rs(now)));
rtg(rs(now))^=1;
}
}
if(ctg(now)!=INT_MAX)
{
if(ls(now))
{
sum(ls(now))=sz(ls(now))*ctg(now);
val(ls(now))=ctg(now);
lsm(ls(now))=rsm(ls(now))=max(sum(ls(now)),0);
sub(ls(now))=max(val(ls(now)),sum(ls(now)));
ctg(ls(now))=ctg(now);
}
if(rs(now))
{
sum(rs(now))=sz(rs(now))*ctg(now);
val(rs(now))=ctg(now);
lsm(rs(now))=rsm(rs(now))=max(sum(rs(now)),0);
sub(rs(now))=max(val(rs(now)),sum(rs(now)));
ctg(rs(now))=ctg(now);
}
}
rtg(now)=false,ctg(now)=INT_MAX;
}
int merge(int x,int y)
{
if(x) pushdown(x);
if(y) pushdown(y);
if(!x||!y) return x+y;
if(rnd(x)<rnd(y))
{
rs(x)=merge(rs(x),y);
update(x);
return x;
}
else
{
ls(y)=merge(x,ls(y));
update(y);
return y;
}
}
void split(int now,int k,int &x,int &y)
{
if(!now) x=y=0;
else
{
pushdown(now);
if(sz(ls(now))>=k)
{
y=now;
split(ls(y),k,x,ls(y));
}
else
{
x=now;
split(rs(x),k-sz(ls(now))-1,rs(x),y);
}
update(now);
}
}
void recycle(int now)
{
if(!H[now]) dustbin.push(now),H[now]=false;
if(ls(now)) recycle(ls(now));
if(rs(now)) recycle(rs(now));
}
int build(int now)
{
stack<int>S;
int last;
for(int i=1;i<=now;i++)
{
int x=new_node(a[i]);last=0;
while(!S.empty()&&rnd(S.top())>rnd(x)) last=S.top(),update(S.top()),S.pop();
if(!S.empty()) rs(S.top())=x;
ls(x)=last,S.push(x);
}
while(!S.empty()) last=S.top(),update(S.top()),S.pop();
return last;
}
int main()
{
srand(19260817);
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
rt=build(n);
while(m--)
{
char opt[20];
scanf("%s",opt);
if(opt[0]=='I')
{
int x,y,p,t;
p=read(),t=read();
split(rt,p,x,y);
for(int i=1;i<=t;i++) a[i]=read();
rt=merge(merge(x,build(t)),y);
}
else if(opt[0]=='D')
{
int x,y,z,p,t;
p=read(),t=read();
split(rt,p-1,x,y);
split(y,t,y,z);
recycle(y);
rt=merge(x,z);
}
else if(opt[0]=='R')
{
int x,y,z,p,t;
p=read(),t=read();
split(rt,p-1,x,y);
split(y,t,y,z);
swap(ls(y),rs(y));
swap(lsm(y),rsm(y));
rtg(y)^=1;
rt=merge(merge(x,y),z);
}
else if(opt[0]=='G')
{
int x,y,z,p,t;
p=read(),t=read();
split(rt,p-1,x,y);
split(y,t,y,z);
printf("%d\n",sum(y));
rt=merge(merge(x,y),z);
}
else if(opt[0]=='M')
{
if(opt[2]=='K')
{
int x,y,z,p,t,tmp;
p=read(),t=read(),tmp=read();
split(rt,p-1,x,y);
split(y,t,y,z);
ctg(y)=val(y)=tmp,sum(y)=sz(y)*ctg(y);
lsm(y)=rsm(y)=sub(y)=max(val(y),sum(y));
rt=merge(merge(x,y),z);
}
else if(opt[2]=='X') printf("%d\n",sub(rt));
}
}
return 0;
}
Luogu P2042 [NOI2005]维护数列(平衡树)的更多相关文章
- BZOJ 1500 Luogu P2042 [NOI2005] 维护数列 (Splay)
手动博客搬家: 本文发表于20180825 00:34:49, 原地址https://blog.csdn.net/suncongbo/article/details/82027387 题目链接: (l ...
- Luogu P2042 [NOI2005]维护数列
题目描述 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线' _ '表示实际输入文件中的空格) 输入输出格式 输入格式: 输入文件的第 1 行包含两个数 N 和 M, ...
- 洛谷 P2042 [NOI2005]维护数列-Splay(插入 删除 修改 翻转 求和 最大的子序列)
因为要讲座,随便写一下,等讲完有时间好好写一篇splay的博客. 先直接上题目然后贴代码,具体讲解都写代码里了. 参考的博客等的链接都贴代码里了,有空再好好写. P2042 [NOI2005]维护数列 ...
- P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]
P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...
- P2042 [NOI2005]维护数列 && Splay区间操作(四)
到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...
- [NOI2005]维护数列——平衡树观止
本题题解并不详细,不推荐大家看这一篇. 可以看这篇 题目描述 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格) 100%的数据中, ...
- 洛谷P2042 [NOI2005]维护数列
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #in ...
- P2042 [NOI2005]维护数列
思路 超级恶心的pushdown 昏天黑地的调 让我想起了我那前几个月的线段树2 错误 这恶心的一道题终于过了 太多错误,简直说不过来 pushup pushdown 主要就是这俩不太清晰,乱push ...
- 数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列
339. [NOI2005] 维护数列 时间限制:3 s 内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际 ...
随机推荐
- SP2713 GSS4 - Can you answer these queries IV(线段树)
传送门 解题思路 大概就是一个数很少次数的开方会开到\(1\),而\(1\)开方还是\(1\),所以维护一个和,维护一个开方标记,维护一个区间是否全部为\(1/0\)的标记.然后每次修改时先看是否有全 ...
- HDU4578-代码一点都不长的线段树
(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 题意:传送门 原题目描述在最下面. 4种操作,1:区间加法,2:区间乘法,3:区间的所有数都变成一个数,4:访问区间每个数的p次方 ...
- python入门 元组tuple (三)
什么if while语句了 列表字典都太简单了 ,我直接跳过了, 开始写tuple了 增 元组格式是写在括号里,注意与列表(写在中括号里)的区别 tup1 = ('math', 'beijing', ...
- Python 爬虫-抓取小说《鬼吹灯之精绝古城》
想看小说<鬼吹灯之精绝古城>,可是网页版的好多广告,还要一页一页的翻,还无法复制,于是写了个小爬虫,保存到word里慢慢看. 代码如下: """ 爬取< ...
- UVA 240 Variable Radix Huffman Encoding
题目链接:https://vjudge.net/problem/UVA-240 题目大意 哈夫曼编码是一种最优编码方法.根据已知源字母表中字符出现的频率,将源字母表中字符编码为目标字母表中字符,最优的 ...
- Metasploit 如何使用Exploits(漏洞)
在Metasploit中选择一个漏洞利用程序将'exploit'和'check'命令添加到msfconsole. msf > use exploit/windows/smb/ms09_050_s ...
- 5-Python操作MySQL步骤
1.引入模块 在py文件中引入pymysql模块 from pymysql import *(connect) 2.创建connection连接对象 conn=connect(参数列表) 参数host ...
- Substring UVA - 11468 AC自动机+概率DP
题意: 给出一些字符和各自对应的选择概率,随机选择L次后得到一个长度为L的随机字符串S. 给出K个模板串,计算S不包含任何一个模板串的概率 dp[i][j]表示走到AC自动机 i 这个节点 还需要走 ...
- 【csp】2018-3
第一题 跳一跳 题目: 题意:浅显.qwq 题解:2计数+1,到1就清空计数. 代码: #include<iostream> #include<cstdio> #include ...
- 增量+全量备份SVN服务器
#!/bin/bash # 获取当前是星期几 DAY=$(date +%w) # 获取当前的日期 DATE=$(date '+%Y-%m-%d-%H-%M') # 获取当前版本库中最新的版本 CURR ...