题目:

题目背景

NOI2005 DAY1 T2

题目描述

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

输入格式

第 1 行包含两个数 N 和 M ,N 表示初始时数列中数的个数,M 表示要进行的操作数目。
第 2 行包含 N 个数字,描述初始时的数列。
以下 M 行,每行一条命令,格式参见问题描述中的表格。

输出格式

对于输入数据中的 GET-SUM 和 MAX-SUM 操作,向输出文件依次打印结果,每个答案(数字)占一行。

样例数据 1

输入  []

 

输出

备注

【样例说明】
初始时,我们拥有数列:
2 -6 3 5 1 -5 -3 6 3

执行操作 GET-SUM 5 4 ,表示求出数列中从第 5 个数开始连续 4 个数字之和,如下图中的灰色部分 1+(-5)+(-3)+6 = -1:
2 -6 3 5 1 -5 -3 6 3

执行操作 MAX-SUM ,表示要求求出当前数列中最大的一段和,即如下图所示,应为 3+5+1+(-5)+(-3)+6+3 = 10:
2 -6 3 5 1 -5 -3 6 3

执行操作 INSERT 8 3 -5 7 2,即在数列中第 8 个数字后插入 -5 7 2,如下所示的灰色部分:
2 -6 3 5 1 -5 -3 6 -5 7 2 3

执行操作 DELETE 12 1,表示删除第 12 个数字,即最后一个:
2 -6 3 5 1 -5 -3 6 -5 7 2

执行操作 MAKE-SAME 3 3 2 ,表示从第 3 个数开始的 3 个数字,即下图中的灰色部分,统一修改为 2 :
2 -6 3 5 1 -5 -3 6 -5 7 2
    改为
2 -6 2 2 2 -5 -3 6 -5 7 2

执行操作 REVERSE 3 6,表示取出数列中从第 3 个数开始的连续 6 个数:

如上所示的灰色部分 2 2 2 -5 -3 6 ,翻转后得到 6 -3 -5 2 2 2 ,并放回原来位置:
2 -6 6 -3 -5 2 2 2 -5 7 2

最后执行 GET-SUM 5 4 和 MAX-SUM ,不难得到答案 1 和 10 。

【评分方法】
本题设有部分分,对于每一个测试点:

  • 如果你的程序能在输出文件正确的位置上打印 GET-SUM 操作的答案,你可以得到该测试点 60% 的分数;
  • 如果你的程序能在输出文件正确的位置上打印 MAX-SUM 操作的答案,你可以得到该测试点 40% 的分数;
  • 以上两条的分数可以叠加,即如果你的程序正确输出所有 GET-SUM 和 MAX-SUM 操作的答案,你可以得到该测试点 100% 的分数。

请注意:如果你的程序只能正确处理某一种操作,请确定在输出文件正确的位置上打印结果,即必须为另一种操作留下对应的行,否则我们不保证可以正确评分。

【数据规模和约定】 
你可以认为在任何时刻,数列中至少有 1 个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
50% 的数据中,任何时刻数列中最多含有 30,000 个数;
100% 的数据中,任何时刻数列中最多含有 500,000 个数。
100% 的数据中,任何时刻数列中任何一个数字均在 [-1,000,1,000] 内 。 
100% 的数据中,M≤20,000,插入的数字总数不超过 4,000,000 个,输入文件大小不超过 20 MBytes 。

题解:

splay大boss题····能打得来splay操作基本没问题了·····

维护lm,rm,mx,sum,size分别表示包含该节点子树代表的区间的左端点的最大序列和,包含该节点子树代表的区间右端点的最大序列和,totsum:此区间的最大序列和最大值,以及该节点子树的和的总和和该节点子树的大小

然后就是各种提取区间操作了,在此引用Zig_zag题解,%%%%

注:标准结构:区间右端点+1(R)为根,区间左端点-1(L)为根的左儿子,这样目标区间就是L的右儿子,这种形式以后都用"标准结构"代替。

插入操作:先把需要插入的序列建成一个小平衡树(递归),转出标准结构,插到L的右儿子上就行了。

删除操作:转出标准结构,把L的右儿子切下来就行了(注意因为要回收空间,所以还是把要切的子树遍历了一遍,把这颗树上的节点标号入栈)。

覆盖操作:转出标准结构,把L的右儿子打上覆盖标记cov(以后下传的时候把节点的值改为cov的值,sum变为cov*size,la=ra=ma变为cov和sum中较大的一个,因为有负数的情况)

翻转操作:转出标准结构,把L的右儿子打上翻转标记rev(以后下传的时候要交换左右儿子并且交换la和ra)

求和操作:转出标准结构,答案就是L的右儿子的sum

最大值操作:转出标准结构,答案就是L的右儿子的ma

区间操作的时候一定要明白一点,就是打标记的同时做修改,就是说当一个点带了标记的时候,它已经被修改过了。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=;
const int inf=0x3f3f3f3f;
int ans,n,m,father[N],son[N][],rev[N],sum[N],lm[N],rm[N],mx[N],cov[N],size[N],key[N],a[N],que[N],tail,tot,st,ed,root;
char s[];
inline int R()
{
char c;int f=,i=;
for(c=getchar();(c<''||c>'')&&c!='-';c=getchar());
if(c=='-') i=-,c=getchar();
for(;c<=''&&c>='';c=getchar())
f=(f<<)+(f<<)+c-'';
return f*i;
}
void update(int x)
{
if (!x) return;
lm[x]=max(lm[son[x][]],sum[son[x][]]+key[x]+max(,lm[son[x][]]));
rm[x]=max(rm[son[x][]],sum[son[x][]]+key[x]+max(,rm[son[x][]]));
mx[x]=max(max(mx[son[x][]],mx[son[x][]]),key[x]+max(,rm[son[x][]])+max(,lm[son[x][]]));
sum[x]=sum[son[x][]]+sum[son[x][]]+key[x];
size[x]=size[son[x][]]+size[son[x][]]+;
}
inline void reverse(int now)
{
if(!now) return;
swap(son[now][],son[now][]);
swap(lm[now],rm[now]);
rev[now]^=;
}
inline void cover(int now,int v)
{
if(!now) return;
sum[now]=size[now]*v;key[now]=cov[now]=v;
lm[now]=rm[now]=mx[now]=max(sum[now],v);
}
void pushdown(int x)
{
if (!x) return;
if (rev[x])
{
reverse(son[x][]);
reverse(son[x][]);
rev[x]=;
}
if (cov[x]!=-inf)
{
cover(son[x][],cov[x]);
cover(son[x][],cov[x]);
cov[x]=-inf;
}
}
inline int get(int now)
{
return son[father[now]][]==now;
}
inline void rotate(int now)
{
int fa=father[now],ofa=father[fa],which=get(now);
son[fa][which]=son[now][which^],father[son[fa][which]]=fa;
son[now][which^]=fa,father[fa]=now,father[now]=ofa;
if(ofa) son[ofa][son[ofa][]==fa]=now;
update(fa),update(now);
}
inline void relax(int now,int to)
{
if(now!=to) relax(father[now],to);
pushdown(now);
}
inline void splay(int now,int to)
{
relax(now,to);
while(father[now]!=to)
{
if(father[father[now]]!=to) rotate(get(now)==get(father[now])?father[now]:now);
rotate(now);
}
if(!to) root=now;
}
inline int pick()
{
if(tail) return que[tail--];
else return ++tot;
}
int produce(int x)
{
int t=pick();
key[t]=a[x];
cov[t]=-inf;
rev[t]=;
lm[t]=rm[t]=mx[t]=-inf;
return t;
}
inline int find(int now,int k)
{
pushdown(now);
if(size[son[now][]]>=k) return find(son[now][],k);
else if(size[son[now][]]+==k) return now;
else return find(son[now][],k-size[son[now][]]-);
}
int build(int l,int r)
{
int mid=(l+r)>>,le=,ri=;
if (l<mid) le=build(l,mid-);
int t=produce(mid);
if (r>mid) ri=build(mid+,r);
if (le) son[t][]=le,father[le]=t;
if (ri) son[t][]=ri,father[ri]=t;
update(t);
return t;
}
void del(int &x)
{
if (!x) return;
que[++tail]=x;
father[x]=;
del(son[x][]);
del(son[x][]);
lm[x]=rm[x]=mx[x]=-inf;
x=;
}
int main()
{
//freopen("a.in","r",stdin);
n=R();m=R();a[st=]=;a[ed=n+]=;
for(int i=;i<=n+;i++) a[i]=R();
rm[]=lm[]=mx[]=-inf;
root=build(,n+);int x,y,z,l,r;
for (int i=;i<=m;i++)
{
scanf("%s",s);
if (s[]=='I')
{
x=R(),y=R();
l=find(root,x+); r=find(root,x+);
splay(r,); splay(l,root);
for (int j=;j<=y;j++)
a[j]=R();
int tmp=build(,y);
father[tmp]=l; son[l][]=tmp;
update(l); update(r);
splay(tmp,root);
}
if (s[]=='D')
{
x=R(),y=R();
r=find(root,x+y+);splay(r,);
l=find(root,x);splay(l,root);
del(son[l][]);
update(l); update(r);
}
if (s[]=='M'&&s[]=='K')
{
x=R(),y=R(),z=R();
r=find(root,x+y+);splay(r,);
l=find(root,x);splay(l,root);
cover(son[l][],z);
}
if (s[]=='R')
{
x=R(),y=R();
r=find(root,x+y+);splay(r,);
l=find(root,x);splay(l,root);
reverse(son[l][]);
}
if (s[]=='G')
{
x=R(),y=R();
r=find(root,x+y+);splay(r,);
l=find(root,x);splay(l,root);
ans=sum[son[l][]];
printf("%d\n",ans);
}
if (s[]=='M'&&s[]=='X')
{
splay(ed,); splay(st,root);
ans=mx[son[st][]];
printf("%d\n",ans);
}
}
return ;
}
05-15 07:08