请写一个程序,要求维护一个数列,支持以下 6 种操作:
编号 | 名称 | 格式 | 说明 |
---|---|---|---|
1 | 插入 | INSERT posi tot c1 c2⋯ctot | 在当前数列的第 posi 个数字后插入 tot 个数字:c1,c2⋯ctot;若在数列首插入,则 posi 为 0 |
2 | 删除 | DELETE posi tot | 从当前数列的第 posi 个数字开始连续删除 tot 个数字 |
3 | 修改 | MAKE-SAME posi tot c | 从当前数列的第 posi 个数字开始的连续 tot 个数字统一修改为 c |
4 | 翻转 | REVERSE posi tot | 取出从当前数列的第 posi 个数字开始的 tot 个数字,翻转后放入原来的位置 |
5 | 求和 | GET-SUM posi tot | 计算从当前数列的第 posi 个数字开始的 tot 个数字的和并输出 |
6 | 求最大子列和 | MAX-SUM | 求出当前数列中和最大的一段子列,并输出最大和 |
输入描述
第一行包含两个整数 N 和 M,N 表示初始时数列中数的个数,M 表示要进行的操作数目。
第二行包含 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% 的数据,任何时刻数列中最多含有 3×104 个数。
对于 100% 的数据,任何时刻数列中最多含有 5×105 个数,任何时刻数列中任何一个数字均在 [−103,103] 内,1≤M≤2×104,插入的数字总数不超过 4×106。
#include<bits/stdc++.h>
using namespace std;
const int N=500010,INF=1e9;
int n,m;
struct Node
{
int s[2],p,v;
int rev;//是否翻转
int same;//是否替换成同一个数
int size;//大小
int sum;//区间和
int ms;//区间最大子序列
int ls;//前缀最大序列
int rs;//后缀最大序列
void init(int _v,int _p)
{
s[0]=s[1]=0;
p=_p;
v=_v;
rev=same=0;
size=1;
sum=ms=v;
ls=rs=max(v,0);
}
}tr[N];
int root,nodes[N],tt;//回收站
int w[N];//序列
void pushup(int x)
{
auto &u=tr[x],&l=tr[u.s[0]],&r=tr[u.s[1]];
u.size=l.size+r.size+1;
u.sum=l.sum+r.sum+u.v;
u.ls=max(l.ls,l.sum+u.v+r.ls);
u.rs=max(r.rs,r.sum+u.v+l.rs);
u.ms=max(max(l.ms,r.ms),l.rs+u.v+r.ls);
}
void pushdown(int x)
{
auto &u=tr[x],&l=tr[u.s[0]],&r=tr[u.s[1]];
if(u.same)//如果这个区间变成同一个数,那翻转不翻转都一样
{
u.same=u.rev=0;
if(u.s[0])
l.same=1,l.v=u.v,l.sum=l.v*l.size;
if(u.s[1])
r.same=1,r.v=u.v,r.sum=r.v*r.size;
if(u.v>0)
{
if(u.s[0])
l.ms=l.ls=l.rs=l.sum;
if(u.s[1])
r.ms=r.ls=r.rs=r.sum;
}
else
{
if(u.s[0])
l.ms=l.v,l.ls=l.rs=0;
if(u.s[1])
r.ms=r.v,r.ls=r.rs=0;
}
}
else if(u.rev)
{
u.rev=0,l.rev^=1,r.rev^=1;
swap(l.ls,l.rs),swap(r.ls,r.rs);
swap(l.s[0],l.s[1]),swap(r.s[0],r.s[1]);
}
}
void rotate(int x)//左旋右旋
{
int y=tr[x].p;
int z=tr[y].p;
int k=tr[y].s[1]==x;
tr[z].s[tr[z].s[1]==y]=x,tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y);
pushup(x);
}
void splay(int x,int k)
{
while(tr[x].p!=k)
{
int y=tr[x].p;
int z=tr[y].p;
if(z!=k)
{
if((tr[y].s[1]==x)^(tr[z].s[1]==y))
rotate(x);
else
rotate(y);
}
rotate(x);
}
if(!k)
root=x;
}
int get_k(int k)
{
int u=root;
while(u)
{
pushdown(u);
if(tr[tr[u].s[0]].size>=k)
u=tr[u].s[0];
else if(tr[tr[u].s[0]].size+1==k)
return u;
else
{
k-=tr[tr[u].s[0]].size+1;
u=tr[u].s[1];
}
}
}
int build(int l,int r,int p)
{
int mid=l+r>>1;
int u=nodes[tt--];//根节点
tr[u].init(w[mid],p);
if(l<mid)
tr[u].s[0]=build(l,mid-1,u);
if(mid<r)
tr[u].s[1]=build(mid+1,r,u);
pushup(u);
return u;
}
void dfs(int u)//内存回收
{
if(tr[u].s[0])
dfs(tr[u].s[0]);
if(tr[u].s[1])
dfs(tr[u].s[1]);
nodes[++tt]=u;
}
int main()
{
for(int i=1;i<N;i++) nodes[++tt]=i;
scanf("%d%d",&n,&m);
tr[0].ms=-INF;
w[0]=w[n+1]=-INF;//哨兵节点
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
root=build(0,n+1,0);
char op[20];
while(m--)
{
scanf("%s",op);
if(!strcmp(op,"INSERT"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
for(int i=0;i<tot;i++) scanf("%d",&w[i]);
int l=get_k(posi+1);//由于有哨兵节点,所以是posi+1
int r=get_k(posi+2);
splay(l,0);
splay(r,l);
int u=build(0,tot-1,r);
tr[r].s[0]=u;
pushup(r);
pushup(l);
}
else if(!strcmp(op,"DELETE"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi);
int r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
dfs(tr[r].s[0]);//将r的左子树删掉
tr[r].s[0]=0;
pushup(r);
pushup(l);
}
else if(!strcmp(op,"MAKE-SAME"))
{
int posi,tot,c;
scanf("%d%d%d",&posi,&tot,&c);
int l=get_k(posi);
int r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
auto &son=tr[tr[r].s[0]];
son.same=1;
son.v=c;
son.sum=c*son.size;
if(c>0)
son.ms=son.ls=son.rs=son.sum;
else
son.ms=c,son.ls=son.rs=0;
pushup(r);
pushup(l);
}
else if(!strcmp(op,"REVERSE"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi);
int r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
auto &son=tr[tr[r].s[0]];
son.rev^=1;
swap(son.ls,son.rs);//反转的时候区间最大前缀变成后缀,最大后缀变成前缀
swap(son.s[0],son.s[1]);//交换左右结点
pushup(r);
pushup(l);
}
else if(!strcmp(op,"GET-SUM"))
{
int posi,tot;
scanf("%d%d",&posi,&tot);
int l=get_k(posi);
int r=get_k(posi+tot+1);
splay(l,0);
splay(r,l);
printf("%d\n",tr[tr[r].s[0]].sum);
}
else
printf("%d\n",tr[root].ms);
}
return 0;
}