BZOJ 1500/Luogu 2042 - 维修数列 - [NOI2005][Splay]
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1500
题目链接:https://www.luogu.org/problemnew/show/P2042
Description
请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格
.png)
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
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
Sample Output
-1
10
1
10
HINT
题解:
Splay模板题。
其中,关于如何搞定求区间最大连续子列和的问题,可以参考线段树的做法:UVALive 3938 - "Ray, Pass me the dishes!" - [最大连续子列和+线段树](通过分治+最大前缀和+最大后缀和共同维护得到最大连续子列和)(感慨一下,已经想不起是哪个时候做的这道题了,时光飞逝啊……)。
关于区间翻转,则是Splay老生常谈的事情了,一个 $rev$ 标记搞定。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=5e5+; int n,m;
int a[maxn]; /******************************** splay - st ********************************/
#define Key_value ch[ch[root][1]][0]
int root,nodecnt;
int par[maxn],ch[maxn][];
int key[maxn],sum[maxn],siz[maxn];
int mxpre[maxn],mxsuf[maxn],mxsub[maxn]; //最大前缀和,最大后缀和,最大连续子列和
bool alt[maxn],rev[maxn]; //修改标记,反转标记
int pool[maxn],poolsize; //节点回收
void NewNode(int &x,int p,int k)
{
if(poolsize>) x=pool[--poolsize];
else x=++nodecnt;
par[x]=p;
ch[x][]=ch[x][]=;
key[x]=sum[x]=k;
mxpre[x]=mxsuf[x]=mxsub[x]=k;
siz[x]=;
alt[x]=rev[x]=;
}
void Update_Rev(int x)
{
if(x==) return;
swap(ch[x][],ch[x][]);
swap(mxpre[x],mxsuf[x]);
rev[x]^=;
}
void Update_Alt(int x,int val)
{
if(x==) return;
key[x]=val;
sum[x]=siz[x]*val;
mxpre[x]=mxsuf[x]=mxsub[x]=max(val,val*siz[x]);
alt[x]=;
}
void Pushup(int x)
{
int ls=ch[x][],rs=ch[x][];
siz[x]=siz[ls]+siz[rs]+;
sum[x]=sum[ls]+sum[rs]+key[x];
mxpre[x]=max(mxpre[ls],sum[ls]+key[x]+max(,mxpre[rs]));
mxsuf[x]=max(mxsuf[rs],max(,mxsuf[ls])+key[x]+sum[rs]);
mxsub[x]=max(max(mxsub[ls],mxsub[rs]),max(,mxsuf[ls])+key[x]+max(,mxpre[rs]));
}
void Pushdown(int x)
{
if(rev[x])
{
Update_Rev(ch[x][]);
Update_Rev(ch[x][]);
rev[x]=;
}
if(alt[x])
{
Update_Alt(ch[x][],key[x]);
Update_Alt(ch[x][],key[x]);
alt[x]=;
}
}
void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig
{
int y=par[x];
ch[y][!type]=ch[x][type]; par[ch[x][type]]=y;
if(par[y]) ch[par[y]][(ch[par[y]][]==y)]=x;
par[x]=par[y];
ch[x][type]=y; par[y]=x;
Pushup(y); Pushup(x);
}
void Splay(int x,int goal)
{
while(par[x]!=goal)
{
if(par[par[x]]==goal) Rotate(x,ch[par[x]][]==x); //左孩子zig,右孩子zag
else
{
int y=par[x];
int type=(ch[par[y]][]==y); //type=0,y是右孩子;type=1,y是左孩子
if(ch[y][type]==x)
{
Rotate(x,!type);
Rotate(x,type);
}
else
{
Rotate(y,type);
Rotate(x,type);
}
}
}
if(goal==) root=x;
}
int Get_Kth(int x,int k) //得到第k个节点
{
Pushdown(x);
int t=siz[ch[x][]]+;
if(t==k) return x;
if(t>k) return Get_Kth(ch[x][],k);
else return Get_Kth(ch[x][],k-t);
}
void Build(int &x,int l,int r,int par) //建树,先建立中间结点,再建两端的方法
{
if(l>r) return;
int mid=(l+r)/;
NewNode(x,par,a[mid]);
Build(ch[x][],l,mid-,x);
Build(ch[x][],mid+,r,x);
Pushup(x);
}
void Init() //初始化,前后各加一个空节点
{
root=nodecnt=poolsize=;
par[]=ch[][]=ch[][]=;
key[]=sum[]=siz[]=;
alt[]=rev[]=;
mxpre[]=mxsuf[]=mxsub[]=-INF;
NewNode(root,,-INF); //头部加入一个空位
NewNode(ch[root][],root,-INF); //尾部加入一个空位
Build(Key_value,,n,ch[root][]);
Pushup(ch[root][]);
Pushup(root);
} void Insert(int p,int tot)
{
for(int i=;i<=tot;i++) scanf("%d",&a[i]);
Splay(Get_Kth(root,p++),); //p伸展到根
Splay(Get_Kth(root,p++),root); //p的后继p+1伸展到根的右孩子
Build(Key_value,,tot,ch[root][]);
Pushup(ch[root][]);
Pushup(root);
} void Collect(int x) //回收节点x统领的子树
{
if(x==) return;
pool[poolsize++]=x;
Collect(ch[x][]);
Collect(ch[x][]);
}
void Delete(int p,int tot)
{
Splay(Get_Kth(root,p-+),); //伸展到根
Splay(Get_Kth(root,p+tot+),root); //伸展到根的右孩子
Collect(Key_value);
par[Key_value]=;
Key_value=;
Pushup(ch[root][]);
Pushup(root);
} void Alter(int p,int tot,int c) //修改[p,p+tot)为k
{
Splay(Get_Kth(root,p-+),); //伸展到根
Splay(Get_Kth(root,p+tot+),root); //伸展到根的右孩子
Update_Alt(Key_value,c);
Pushup(ch[root][]);
Pushup(root);
} void Reverse(int p,int tot) //反转[p,p+tot)区间
{
Splay(Get_Kth(root,p-+),);
Splay(Get_Kth(root,p+tot+),root);
Update_Rev(Key_value);
Pushup(ch[root][]);
Pushup(root);
} int Get_Sum(int p,int tot)
{
Splay(Get_Kth(root,p-+),);
Splay(Get_Kth(root,p+tot+),root);
return sum[Key_value];
} int Get_MaxSub(int p,int tot)
{
Splay(Get_Kth(root,p-+),);
Splay(Get_Kth(root,p+tot+),root);
return mxsub[Key_value];
}
/******************************** splay - ed ********************************/ int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&a[i]);
Init(); char op[];
int pos,tot,c;
while(m--)
{
scanf("%s",op);
switch(op[]&op[]|op[])
{
case ('I'&'N'|'S'):
scanf("%d%d",&pos,&tot);
Insert(pos,tot);
break;
case ('D'&'E'|'L'):
scanf("%d%d",&pos,&tot);
Delete(pos,tot);
break;
case ('M'&'A'|'K'):
scanf("%d%d%d",&pos,&tot,&c);
Alter(pos,tot,c);
break;
case ('R'&'E'|'V'):
scanf("%d%d",&pos,&tot);
Reverse(pos,tot);
break;
case ('G'&'E'|'T'):
scanf("%d%d",&pos,&tot);
printf("%d\n",Get_Sum(pos,tot));
break;
case ('M'&'A'|'X'):
printf("%d\n",Get_MaxSub(,siz[root]-));
break;
}
}
}
BZOJ 1500/Luogu 2042 - 维修数列 - [NOI2005][Splay]的更多相关文章
- [bzoj1500 维修数列](NOI2005) (splay)
真的是太弱了TAT...光是把代码码出来就花了3h..还调了快1h才弄完T_T 号称考你会不会splay(当然通过条件是1h内AC..吓傻)... 黄学长的题解:http://hzwer.com/28 ...
- bzoj 1500 [NOI 2005] 维修数列
题目大意不多说了 貌似每个苦逼的acmer都要做一下这个splay树的模版题目吧 还是有很多操作的,估计够以后当模版了.... #include <cstdio> #include < ...
- 【BZOJ1500】【NOI2005】维修数列(Splay)
[BZOJ1500][NOI2005]维修数列(Splay) 题面 不想再看见这种毒瘤题,自己去BZOJ看 题解 Splay良心模板题 真的很简单 我一言不发 #include<iostream ...
- BZOJ 1500 Luogu P2042 [NOI2005] 维护数列 (Splay)
手动博客搬家: 本文发表于20180825 00:34:49, 原地址https://blog.csdn.net/suncongbo/article/details/82027387 题目链接: (l ...
- 【BZOJ】1500: [NOI2005]维修数列(splay+变态题)
http://www.lydsy.com/JudgeOnline/problem.php?id=1500 模板不打熟你确定考场上调试得出来? 首先有非常多的坑点...我遇到的第一个就是,如何pushu ...
- BZOJ 1500 维修数列【Splay】
注意:1,内存限制,所以需要回收删除的点 2,当前节点的左连续区间和最大值=max(左子树的左连续区间和最大值,左子树的总和+当节点的值+max(右子树的左连续区间和最大值,0)):右连续区间和最大值 ...
- NOI2005维修数列(splay)
题目描述: Description 请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格 Input 输入的第1 行包含两个数N 和M( ...
- 洛谷 2042 BZOJ 1500 NOI 2005 维护数列
[题意概述] 维护一个数列,要求支持以下6种操作: [题解] 大Boss...可以用Treap解决 需要用到垃圾回收.线性建树. #include<cstdio> #include< ...
- 【BZOJ1500】维修数列(splay)
题意: 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的 ...
随机推荐
- 合理设置apache httpd的最大连接数
来自:http://hi.baidu.com/rainchen/blog/item/095f0a551fa802c5b645ae46.html 手头有一个网站在线人数增多,访问时很慢.初步认为是服务器 ...
- 9.1 翻译系列:数据注解特性之----Table【EF 6 Code-First 系列】
原文地址:http://www.entityframeworktutorial.net/code-first/table-dataannotations-attribute-in-code-first ...
- C++ 重载运算符和重载函数
C++ 重载运算符和重载函数 C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载. 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是 ...
- C# 版本的24点实现
C# 版本的24点实现. 已经实现基本功能,可以正确的算 3, 3, 8, 8 这类组合. 稍加修改就可以支持任意数目的操作数和操作符组合形成的四则运算表达式,不限于24点. 代码还比较简单粗糙,晚一 ...
- iOS的动态代理模式的实现
动态代理模式的应用很多,特别是在不能修改被代理类的前提下,要对执行某些方法时需要打log或者捕捉异常等处理时,是一个非常方便的方法.只需要少量修改客户端(场景类)代码和添加一个代理类就可以实现,这个符 ...
- [svc]linux的ip命令操作接口和路由表
参考: https://www.tecmint.com/ip-command-examples/ 学会linux的配置ip,配置网关,添加路由等命令 man ip man ip address man ...
- Source Insight 常用设置
1.背景色选择 要改变背景色Options->preference->windows background->color设置背景色2.解决字符等宽对齐问题 SIS默认字体是VE ...
- Apache Hadoop YARN: 背景及概述
从2012年8月开始Apache Hadoop YARN(YARN = Yet Another Resource Negotiator)成了Apache Hadoop的一项子工程.自此Apache H ...
- cp显示进度条
cp显示进度条 alias cp='rsync -av --progress'
- CodeIgniter中使用CSRF TOKEN的一个坑
事情的经过是这样的,一个自动化扫描工具说我的代码中存在XSS漏洞,什么是XSS不懂的朋友可以看这里 我的代码里面开启CodeIgniter框架的CSRF Token,如下: 很简单,更多详情参考CI官 ...