题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1500

题目链接:https://www.luogu.org/problemnew/show/P2042

Description

请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格

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]的更多相关文章

  1. [bzoj1500 维修数列](NOI2005) (splay)

    真的是太弱了TAT...光是把代码码出来就花了3h..还调了快1h才弄完T_T 号称考你会不会splay(当然通过条件是1h内AC..吓傻)... 黄学长的题解:http://hzwer.com/28 ...

  2. bzoj 1500 [NOI 2005] 维修数列

    题目大意不多说了 貌似每个苦逼的acmer都要做一下这个splay树的模版题目吧 还是有很多操作的,估计够以后当模版了.... #include <cstdio> #include < ...

  3. 【BZOJ1500】【NOI2005】维修数列(Splay)

    [BZOJ1500][NOI2005]维修数列(Splay) 题面 不想再看见这种毒瘤题,自己去BZOJ看 题解 Splay良心模板题 真的很简单 我一言不发 #include<iostream ...

  4. BZOJ 1500 Luogu P2042 [NOI2005] 维护数列 (Splay)

    手动博客搬家: 本文发表于20180825 00:34:49, 原地址https://blog.csdn.net/suncongbo/article/details/82027387 题目链接: (l ...

  5. 【BZOJ】1500: [NOI2005]维修数列(splay+变态题)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1500 模板不打熟你确定考场上调试得出来? 首先有非常多的坑点...我遇到的第一个就是,如何pushu ...

  6. BZOJ 1500 维修数列【Splay】

    注意:1,内存限制,所以需要回收删除的点 2,当前节点的左连续区间和最大值=max(左子树的左连续区间和最大值,左子树的总和+当节点的值+max(右子树的左连续区间和最大值,0)):右连续区间和最大值 ...

  7. NOI2005维修数列(splay)

    题目描述: Description 请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格 Input 输入的第1 行包含两个数N 和M( ...

  8. 洛谷 2042 BZOJ 1500 NOI 2005 维护数列

    [题意概述] 维护一个数列,要求支持以下6种操作: [题解] 大Boss...可以用Treap解决 需要用到垃圾回收.线性建树. #include<cstdio> #include< ...

  9. 【BZOJ1500】维修数列(splay)

    题意: 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的 ...

随机推荐

  1. redhat7.0配置网卡

    1.切换到网卡配置目录: cd /etc/sysconfig/network-scripts 2.编辑网卡信息 如 vim ifcfg-enpos3 TYPE=Ethernet #设备类型 BOOTP ...

  2. Linux 系统实时监控的瑞士军刀 —— Glances

    Linux 系统实时监控的瑞士军刀 —— Glances 对于 RHEL/CentOS/Fedora 发行版 ## RHEL/CentOS 7 64-Bit ## # wget http://dl.f ...

  3. git报ssh variant 'simple' does not support setting port解决办法

      解决办法 在git bash中输入命令 1 git config --global ssh.variant ssh 照着来一遍,肯定解决

  4. 第三部分:Android 应用程序接口指南---第二节:UI---第七章 通知

    第7章 通知 一个通知是一条消息他是显示于你应用程序之外的一个界面中.当你告诉系统要发布一个通知时,它首先作为一个icon出现在通知区域.为了看见通知的细节,用户可以点击通知区域展开一个新的界面.下面 ...

  5. 《转》Babel 入门教程

    ECMAScript 6是JavaScript语言的下一代标准,已经在2015年6月正式发布了.Mozilla公司将在这个标准的基础上,推出JavaScript 2.0.ES6的目标,是使得JavaS ...

  6. 【Python】python3.6中实现同一行动态输出

    print同时需要设置flush=True来动态显示 print("\r[{0}] {1} <- {2}".format(username,time,jdTimeFomat) ...

  7. 【iCore1S 双核心板_FPGA】例程二:GPIO输入实验——识别按键输入

    实验现象: iCore1s 双核心板上与FPGA相连的三色LED(PCB上标示为FPGA·LED),按键按下红灯点亮,松开按键红灯熄灭. 核心源代码: module KEY( input CLK_12 ...

  8. MVC的Membership

    摘自:http://stackoverflow.com/a/16734651/1616023 See the summaries below each quote for a quick answer ...

  9. 服务器部署多个tomcat(Address already in use: JVM_Bind)

    一.修改startup.bat **多个Tomcat同时运行时.不要设置 catalina_home catalina_base classes 环境变量, 修改setclasspath.bat (| ...

  10. 设计模式 -创建型模式 ,python工厂模式 抽象工厂模式(1)

    工厂模式 import xml.etree.ElementTree as etree import json class JSONConnector: def __init__(self, filep ...