luogu P2710 数列
(这是个双倍经验呀!
Description
维护一个可以支持插入、删除、翻转、区间赋值、求和、求值和求最大子段和操作的序列。(真·简洁)
Solution
基本不用什么神奇操作,平衡树硬上就行。(我用的 Splay )
不太一样的是建树可以模仿线段树建法(据说会快一点)
其中,
1.翻转,区间赋值来打两种标记。
2.求最大子段和要设最大子段和,最大前缀和最大后缀三种变量。(不太清楚的可以先去做做小鲦逛公园)
再注意一下,
1.下传标记时,先区间赋值再翻转。
(因为区间赋值改变 \(val\) ,翻转改变 \(id\) , \(id\) 要影响 \(val\) )
2.翻转时,最大前缀和最大后缀要交换。
(因为翻转后,后面的到了前面,前面的到了后面,顺序改变了)
3.平时讲的把一个区间 \([l,r]\) 拉出来是通过把区间外前面的第一个点拉到根后再将区间外后面的第一个点拉到根右孩子树的根上。
实际操作时下标要集体右移一位,因为如果区间为整个数组时要把 \(0\) 拉到根上去,那不就乱了吗!
其实上述情况打文艺平衡树的时候就有了,但再次注意的是这题输入的 \(posi\) 和 \(tot\) 也要在表示区间的时候进行下标的转化。
具体的看看代码吧(马蜂可看,比较清奇,但是长死了,有点烦)
#include<bits/stdc++.h>
#define ls(i) spl[i].ch[0]//左孩子
#define rs(i) spl[i].ch[1]//右孩子
#define fa(i) spl[i].fa//粑粑
#define val(i) spl[i].val//点权值
#define num(i) spl[i].num//子树权值和
#define siz(i) spl[i].siz//字数大小
#define lzy(i) spl[i].lzy//翻转标记
#define mdf(i) spl[i].mdf//区间赋值标记
#define mx(i) spl[i].mx//最大子段和
#define lmx(i) spl[i].lmx//最大前缀
#define rmx(i) spl[i].rmx//最大后缀
//看把孩子逼的
#define reg register
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int INF=0x3f3f3f3f;
int n,m,root,cnt,a[N],id[N];
char s[24];
queue<int> que;
struct Splay{int ch[2],fa,val,num,siz,lzy,mdf,mx,lmx,rmx;}spl[N];
inline int imax(int a,int b){return a>b?a:b;}
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void pushup(int now){
siz(now)=siz(ls(now))+siz(rs(now))+1;
num(now)=num(ls(now))+num(rs(now))+val(now);
int plu=rmx(ls(now))+lmx(rs(now))+val(now);
mx(now)=imax(imax(mx(ls(now)),mx(rs(now))),plu);
lmx(now)=imax(lmx(ls(now)),lmx(rs(now))+num(ls(now))+val(now));
rmx(now)=imax(rmx(rs(now)),rmx(ls(now))+num(rs(now))+val(now));
}
inline void pushdown(int now){
if(mdf(now)){
mdf(now)=lzy(now)=0;
if(ls(now)){
mdf(ls(now))=1;val(ls(now))=val(now);
num(ls(now))=siz(ls(now))*val(ls(now));
}
if(rs(now)){
mdf(rs(now))=1;val(rs(now))=val(now);
num(rs(now))=siz(rs(now))*val(rs(now));
}
if(val(now)>0){
if(ls(now))mx(ls(now))=lmx(ls(now))=rmx(ls(now))=num(ls(now));
if(rs(now))mx(rs(now))=lmx(rs(now))=rmx(rs(now))=num(rs(now));
}
else {
if(ls(now))mx(ls(now))=val(ls(now)),lmx(ls(now))=rmx(ls(now))=0;
if(rs(now))mx(rs(now))=val(rs(now)),lmx(rs(now))=rmx(rs(now))=0;
}
}
if(lzy(now)){
lzy(ls(now))^=1;lzy(rs(now))^=1;lzy(now)^=1;
swap(ls(ls(now)),rs(ls(now)));
swap(ls(rs(now)),rs(rs(now)));
swap(lmx(ls(now)),rmx(ls(now)));
swap(lmx(rs(now)),rmx(rs(now)));
}
}
inline void rotate(int now){
int nxt=fa(now),nnt=fa(nxt);
int k1=rs(nxt)==now,k2=rs(nnt)==nxt;
int pre=spl[now].ch[k1^1];
spl[nnt].ch[k2]=now; fa(now)=nnt;
spl[nxt].ch[k1]=pre; fa(pre)=nxt;
spl[now].ch[k1^1]=nxt; fa(nxt)=now;
pushup(nxt);pushup(now);
}//虽然肯定会跑得慢,但真tm好看
inline void splay(int now,int S){
while(fa(now)!=S){
int nxt=fa(now),nnt=fa(nxt);
int k1=rs(nxt)==now,k2=rs(nnt)==nxt;
if(nnt!=S)(k1^k2)?rotate(now):rotate(nxt);
rotate(now);
}
if(!S)root=now;
}
inline void build(int lt,int rt,int mi){
if(lt>rt)return ;
int mid=(lt+rt)>>1,now=id[mid],pre=id[mi];
if(lt==rt){
siz(now)=1;
if(a[lt]>0)mx(now)=lmx(now)=rmx(now)=a[lt];
else mx(now)=a[lt],lmx(now)=rmx(now)=0;
}
else {
build(lt,mid-1,mid);
build(mid+1,rt,mid);
}
val(now)=a[mid];fa(now)=pre;
pushup(now);
spl[pre].ch[mid>=mi]=now;
}
inline int find(int now,int siz){
pushdown(now);
int sizz=siz(ls(now));
if(siz<=sizz)return find(ls(now),siz);
else if(siz==sizz+1)return now;
else return find(rs(now),siz-sizz-1);
}
inline void insert(int now,int sum){
for(int i=1;i<=sum;++i){
a[i]=read();
if(!que.empty()){
id[i]=que.front();
que.pop();
}
else {
id[i]=++cnt;
}
}
build(1,sum,0);
int mid=(1+sum)>>1,it=id[mid];
int lt=find(root,now+1);
int rt=find(root,now+2);
splay(lt,0);splay(rt,lt);
ls(rt)=it;fa(it)=rt;
pushup(rt);pushup(root);
}
inline void split(int now){
if(!now)return ;
split(ls(now));split(rs(now));
que.push(now);
ls(now)=rs(now)=val(now)=num(now)=siz(now)=0;
lzy(now)=mdf(now)=mx(now)=lmx(now)=rmx(now)=0;
}
inline int divid(int lt,int rt){
lt=find(root,lt);rt=find(root,rt);
//一定要返回去看,因为这些坐标是提前对应好的
splay(lt,0);splay(rt,lt);
return ls(rt);
}
inline void delet(int lt,int rt){
int now=divid(lt,rt),nxt=fa(now);
split(now);
ls(nxt)=0;
pushup(nxt);pushup(root);
}
inline void modify(int lt,int rt,int val){
int now=divid(lt,rt),nxt=fa(now);
mdf(now)=1;val(now)=val;
num(now)=siz(now)*val(now);
if(val>0)mx(now)=lmx(now)=rmx(now)=num(now);
else mx(now)=val,lmx(now)=rmx(now)=0;
pushup(nxt);pushup(root);
}
inline void reverse(int lt,int rt){
int now=divid(lt,rt),nxt=fa(now);
if(!mdf(now)){
lzy(now)^=1;
swap(ls(now),rs(now));
swap(lmx(now),rmx(now));
pushup(nxt);pushup(root);
}
}
inline int getsum(int lt,int rt){
int now=divid(lt,rt);
return num(now);
}
inline int getmxs(int lt,int rt){
lt=find(root,lt);rt=find(root,rt);
//一定要返回去看,因为这些坐标是提前对应好的
splay(lt,0);splay(rt,lt);
return mx(ls(rt));
}
inline int getnum(int lt){
lt=find(root,lt+1);
return val(lt);
}
int main(){
n=read();m=read();
for(int i=2;i<=n+1;++i){
a[i]=read();id[i]=i;
}
mx(0)=a[1]=a[n+2]=-INF;id[1]=1;id[n+2]=n+2;
//因为不能存0,所以集体右移一位了
build(1,n+2,0);cnt=n+2;root=(cnt+1)>>1;
while(m--){
scanf("%s",s);
if(s[0]=='I'){
int posi=read(),tot=read();
insert(posi,tot);
}
else if(s[0]=='D'){
int posi=read(),tot=read();
delet(posi,posi+tot+1);
}
else if(s[2]=='K'){
int posi=read(),tot=read(),c=read();
modify(posi,posi+tot+1,c);
}
else if(s[0]=='R'){
int posi=read(),tot=read();
reverse(posi,posi+tot+1);
}
else if(s[0]=='G'&&s[3]=='-'){
int posi=read(),tot=read();
printf("%d\n",getsum(posi,posi+tot+1));
}
else if(s[0]=='M'&&s[3]=='-'){
int posi=read(),tot=read();
printf("%d\n",getmxs(posi,posi+tot+1));
}
else {
int posi=read();
printf("%d\n",getnum(posi));
}
}
return 0;
}