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行,每行一条命令,格式参见问题描述中的 ...
随机推荐
- ant+svn+tomcat实现自动构建
前段时间用做了一个简单的web api服务,在调试的过程中感觉到手动发布实在是效率低而且容易出错,于是花点时间搞了一下ant+svn+tomcat的自动构建,效果不错,今天拿出来分享一下. 准备工作 ...
- 第三方文本框 在div中显示预览,让指定节点不受外部css影响
例如,富文本框中 ol li 但是我们往往全局样式时候会 让前面的数字不显示,但是富文本框时候,录入,我们需要显示,但是div中就不显示了 我们在预览页面中加上一个指定样式 然后后面 加上!im ...
- Effective Java 第三版——50. 必要时进行防御性拷贝
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- 分库分表利器——sharding-sphere
背景 得不到的东西让你彻夜难眠,没有尝试过的技术让我跃跃欲试. 本着杀鸡焉用牛刀的准则,我们倡导够用就行,不跟风,不盲从. 所以,结果就是我们一直没有真正使用分库分表.曾经好几次,感觉没有分库分表(起 ...
- [svc]sed&awk过滤行及sed常用例子
- sed过滤行 sed '2p' sed '2,5p' sed '2p;3p;4p' - awk过滤行 awk 'NR==2' awk 'NR>=2 && NR <=3' ...
- Source Insight 常用设置
1.背景色选择 要改变背景色Options->preference->windows background->color设置背景色2.解决字符等宽对齐问题 SIS默认字体是VE ...
- FAL_CLIENT和FAL_SERVER参数详解
FAL_CLIENT和FAL_SERVER参数详解 转载:http://openwares.net/database/fal_client_fal_server.html FAL_CLIENT和FAL ...
- linux中的信号机制
概述 Linux信号机制是在应用软件层次上对中断机制的一种模拟,信号提供了一种处理异步事件的方法,例如,终端用户输入中断键(ctrl+c),则会通过信号机制停止一个程序[1]. 这其实就是向那个程序( ...
- Java知多少(15)字符串
从表面上看,字符串就是双引号之间的数据,例如“微学苑”.“http://www.weixueyuan.net”等.在Java中,可以使用下面的方法定义字符串: String stringName ...
- 开发过程中遇到的问题1--------我们的mysql的查询语句时自己写的,没有用oracle的nextvalue函数。所以这里涉及到了并发的问题。
效果http://www.cnblogs.com/wanggangblog/p/4037543.html 很多的采购单会生成.生成的时候会有订单的编号,然后一个采购单的编号是唯一的,怎么生成呢?之前o ...