[NOI2005] 维护数列
题解
注意到有插入、删除、翻转、区间赋值操作,还要维护区间和、最大子段和的信息,用 F H Q T r e a p \rm FHQ Treap FHQTreap 实现文艺平衡树即可。
具体地,你需要对每个节点维护当前节点的值、子树区间和、子树区间内最大字段和、子树区间内靠左端点的最大子段和以及子树区间内靠右端点的最大子段和。还需要翻转、赋值两个懒标记,就可以像写线段树一样用 F H Q T r e a p \rm FHQ Treap FHQTreap 维护这些值。在普通 F H Q T r e a p \rm FHQ Treap FHQTreap 的基础上,你只需要写好 c o v e r \rm cover cover——覆盖懒标记、 p u s h d o w n \rm pushdown pushdown——懒标记下传、 u p d a t e \rm update update——更新节点维护的值,这三个函数,然后在分裂和合并函数的开头对传入的节点进行 p u s h d o w n \rm pushdown pushdown 即可。实现细节就请看代码吧。
然后是一些坑点:
- 如果所有数都是负数,最大子段和不是0,所以维护的区间最大子段和不能和0取 max \max max,并且要把节点0的值赋为负无穷。
- 对一个节点做翻转操作时,不止要把左右儿子交换,而且要交换靠着区间左右端点的最大子段和的值。
- 这题卡空间,加入点的次数远大于实际存在的点的个数,我们要把删除的点的标号回收利用。具体而言,可以开一个栈(“垃圾桶”),存下已删除的树的根,每次从中取出一棵树,把根节点赋为新值,重新利用,然后把左右子树再丢入栈。这样每次删除操作把点放入垃圾桶就是 O ( 1 ) O(1) O(1) 的,比把树上的点暴力拆开入栈快一些。
代码
#include<cstdio>//JZM yyds!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MAXN 1000005
#define INF 1e6
#define lowbit(x) ((x)&(-(x)))
#define MOD 1000000007ll
#define IF it->first
#define IS it->second
#define rg register
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
struct node{
int x,y;node(){}
node(int X,int Y){x=X,y=Y;}
};
struct itn{
int ls,rs,siz,val,a,lz2;
ll ml,ms,mr,s;
bool lz1;itn(){}
itn(int A){
ls=rs=0,siz=1,val=rand()%MAXN,a=s=A;
lz2=INF,ml=mr=max(0,a),ms=a,lz1=0;
}
}t[MAXN];
int root,IN;
inline void cover(int x,bool z1,int z2){
if(!x){t[x].ms=-1e17;return;}
if(z1){
t[x].lz1^=1;
swap(t[x].ls,t[x].rs),swap(t[x].ml,t[x].mr);
}
if(z2<INF){
t[x].a=z2,t[x].s=1ll*z2*t[x].siz;
if(z2>0)t[x].ml=t[x].mr=t[x].ms=1ll*z2*t[x].siz;
else t[x].ml=t[x].mr=0,t[x].ms=z2;
t[x].lz2=z2;
}
}
inline void pushd(int x){
if(t[x].lz1||t[x].lz2<INF){
cover(t[x].ls,t[x].lz1,t[x].lz2);
cover(t[x].rs,t[x].lz1,t[x].lz2);
t[x].lz1=0,t[x].lz2=INF;
}
}
inline void update(int x){
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
t[x].s=t[t[x].ls].s+t[t[x].rs].s+t[x].a;
t[x].ms=max(t[t[x].ls].mr+t[t[x].rs].ml+t[x].a,
max(t[t[x].ls].ms,t[t[x].rs].ms));
t[x].ml=max(0ll,max(t[t[x].ls].ml,t[t[x].ls].s+t[x].a+t[t[x].rs].ml));
t[x].mr=max(0ll,max(t[t[x].rs].mr,t[t[x].rs].s+t[x].a+t[t[x].ls].mr));
}
inline node split(int x,int k){
if(!x||!k)return node(0,x);node res;pushd(x);
if(t[t[x].ls].siz>=k)res=split(t[x].ls,k),t[x].ls=res.y,update(x),res.y=x;
else res=split(t[x].rs,k-t[t[x].ls].siz-1),t[x].rs=res.x,update(x),res.x=x;
return res;
}
inline int mergg(int x,int y){
if(!x||!y)return x|y;int res;pushd(x),pushd(y);
if(t[x].val<t[y].val)t[x].rs=mergg(t[x].rs,y),update(x),res=x;
else t[y].ls=mergg(x,t[y].ls),update(y),res=y;
return res;
}
int n,m;
stack<int>fd;
char s[10];
inline int newp(int a){
if(fd.empty()){
t[++IN]=itn(a);return IN;
}
else{
int tp=fd.top();fd.pop();
int in=tp;
if(t[tp].ls)fd.push(t[tp].ls);
if(t[tp].rs)fd.push(t[tp].rs);
t[in]=itn(a);return in;
}
}
signed main()
{
srand(time(0));
n=read(),m=read(),t[0].ms=-1e17;
for(int i=1;i<=n;i++)root=mergg(root,newp(read()));
for(int D=1;D<=m;D++){
scanf("%s",s+1);
if(s[1]=='I'&&s[3]=='S'){
int pos=read(),tot=read(),in=0;
for(int i=1;i<=tot;i++)
in=mergg(in,newp(read()));
node x=split(root,pos);
root=mergg(x.x,mergg(in,x.y));
}else if(s[1]=='D'&&s[3]=='L'){
int pos=read(),tot=read();
node x=split(root,pos-1),y=split(x.y,tot);
fd.push(y.x);
root=mergg(x.x,y.y);
}else if(s[1]=='M'&&s[3]=='K'){
int pos=read(),tot=read(),c=read();
node x=split(root,pos-1),y=split(x.y,tot);
cover(y.x,0,c),root=mergg(x.x,mergg(y.x,y.y));
}else if(s[1]=='R'&&s[3]=='V'){
int pos=read(),tot=read();
node x=split(root,pos-1),y=split(x.y,tot);
cover(y.x,1,INF),root=mergg(x.x,mergg(y.x,y.y));
}else if(s[1]=='G'&&s[3]=='T'){
int pos=read(),tot=read();
node x=split(root,pos-1),y=split(x.y,tot);
printf("%lld\n",t[y.x].s);
root=mergg(x.x,mergg(y.x,y.y));
}else printf("%lld\n",t[root].ms);
}
return 0;
}