一、题目
二、解法
写一发无旋
treap
\text{treap}
treap的题解吧,我重构了足足三遍,讲一下所有坑点。
如果你对
treap
\text{treap}
treap不是很了解,可以看这个。
操作1 insert
- 这道题不能 O ( n log n ) O(n\log n) O(nlogn)无脑插入,要考虑 O ( n ) O(n) O(n)建树,我们维护一个 h e a p heap heap递增的单调栈,每次加入一个点弹出比它 h e a p heap heap大的,最后一个被弹出的点是他的左儿子,他是弹栈后的栈顶的右儿子,修改儿子后再跑一边整棵树,我们最开始是建一个 h e a p heap heap最小的超级根,真正的根是超级根的右儿子,插入的话我们就把建出来的树与原来的树合并。
- 还有一个问题就是空间开不下,由于数列的长度是限定的,可以写一个内存池,动态拿空间。
操作2 delete
- 把删除的区间分裂出来,然后合并。
操作3 modify
- 把区间拆出来打懒标记,注意他有可能让你区间赋 0 0 0,所以要用 i n f inf inf当没有标记时赋的值。
操作4 reverse
- 以后要维护一个区间最大字段和,翻转时只有左边开始的最大子段和右边开始的最大子段会受影响,应该交换左边开始和右边开始。
操作5 get-sum
- 把要求的区间拆出来,然后输出 sum \text{sum} sum即可。
操作6 最大子段和
- 维护一个左起最大子段,右起最大子段,区间最大子段,讲一讲更新区间最大子段吧,区间最大子段可以从三个地方转移:左儿子最大子段,右儿子最大子段,中间值+左儿子右起最大子段+右儿子左起最大子段,仔细想想这种更新方法可以涵盖所有情况。
- 子段不能为空,所以最大子段有时需要为负,需要判断为负是选不选。
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <ctime>
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN = 500005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,rt,top;char str[20];
int ch[MAXN][2],siz[MAXN],hp[MAXN],fl[MAXN],q[MAXN];
int sum[MAXN],val[MAXN],ml[MAXN],mr[MAXN],mx[MAXN],la[MAXN];
struct Memory_Pool
{
int top,p[MAXN];
Memory_Pool()
{
top=MAXN-1;
for(int i=1;i<=top;i++)
p[i]=i;
}
int New() {return p[top--];}
void Rec(int x) {p[++top]=x;}
}M;//内存池
struct node
{
int p[2];
node() {p[0]=p[1]=0;}
}emp;//两个根的结构体
void up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
ml[x]=max(ml[ch[x][0]],sum[ch[x][0]]+val[x]+max(0,ml[ch[x][1]]));
mr[x]=max(mr[ch[x][1]],sum[ch[x][1]]+val[x]+max(0,mr[ch[x][0]]));
mx[x]=max(max(mx[ch[x][0]],mx[ch[x][1]]),val[x]+max(0,mr[ch[x][0]])+max(0,ml[ch[x][1]]));
}//push_up
void modify(int x,int c)
{
val[x]=c;sum[x]=c*siz[x];
if(c<0)
ml[x]=mr[x]=mx[x]=c;
else
ml[x]=mr[x]=mx[x]=sum[x];
}//区间修改
void flip(int x)
{
swap(ch[x][0],ch[x][1]);
swap(ml[x],mr[x]);
}//区间翻转
void down(int x)
{
if(fl[x])
{
fl[ch[x][0]]^=1;fl[ch[x][1]]^=1;
flip(ch[x][0]);flip(ch[x][1]);
fl[x]=0;
}
if(la[x]!=inf)
{
la[ch[x][0]]=la[ch[x][1]]=la[x];
modify(ch[x][0],la[x]);modify(ch[x][1],la[x]);
la[x]=inf;
}
}//下传标记
node split(int x,int s)
{
if(!x) return emp;
down(x);
node y;
if(siz[ch[x][0]]>=s)
{
y=split(ch[x][0],s);
ch[x][0]=y.p[1];
y.p[1]=x;
}
else
{
y=split(ch[x][1],s-siz[ch[x][0]]-1);
ch[x][1]=y.p[0];
y.p[0]=x;
}
up(x);
return y;
}//分裂
int merge(int x,int y)
{
if(!x || !y) return x+y;
if(hp[x]<hp[y])
{
down(x);
ch[x][1]=merge(ch[x][1],y);
up(x);
return x;
}
down(y);
ch[y][0]=merge(x,ch[y][0]);
up(y);
return y;
}//合并
void reuse(int x)
{
if(!x) return ;
reuse(ch[x][0]);reuse(ch[x][1]);
M.Rec(x);
}//回收空间
void init(int x)
{
if(!x) return ;
init(ch[x][0]);init(ch[x][1]);
up(x);
}//跑一遍新建的树
int build(int n)
{
q[top=1]=1;hp[1]=-inf;
for(int i=1;i<=n;i++)
{
int x=M.New(),t=0;
ch[x][0]=ch[x][1]=fl[x]=0;la[x]=inf;siz[x]=1;
hp[x]=rand();val[x]=sum[x]=ml[x]=mr[x]=mx[x]=read();
while(top && hp[q[top]]>=hp[x]) t=q[top--];
ch[q[top]][1]=x;ch[x][0]=t;
q[++top]=x;
}
init(1);
return ch[1][1];
}//O(n)建树,返回根
int main()
{
srand(time(0));
ml[0]=mr[0]=mx[0]=val[0]=-inf;
n=read();m=read();
rt=build(n);
for(int i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='I')
{
int pos=read(),t=read();
node x=split(rt,pos);
rt=merge(merge(x.p[0],build(t)),x.p[1]);
}
if(str[0]=='D')
{
int pos=read(),t=read();
node x=split(rt,pos-1),y=split(x.p[1],t);
reuse(y.p[0]);
rt=merge(x.p[0],y.p[1]);
}
if(str[0]=='M' && str[2]=='K')
{
int pos=read(),t=read(),c=read();
node x=split(rt,pos-1),y=split(x.p[1],t);
la[y.p[0]]=c;
modify(y.p[0],c);
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
}
if(str[0]=='R')
{
int pos=read(),t=read();
node x=split(rt,pos-1),y=split(x.p[1],t);
fl[y.p[0]]^=1;
flip(y.p[0]);
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
}
if(str[0]=='G')
{
int pos=read(),t=read();
node x=split(rt,pos-1),y=split(x.p[1],t);
printf("%d\n",sum[y.p[0]]);
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
}
if(str[0]=='M' && str[2]=='X')
{
printf("%d\n",mx[rt]);
}
}
}
updata on 2020/1/9:
最近学了
splay
\text{splay}
splay,贴个代码,注意打标记之后要把当前点旋转到根,还有一些坑点代码中有注释。
#include <cstdio>
#include <iostream>
using namespace std;
#define inf 0x3f3f3f3f
const int M = 500005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,rt,a[M],ch[M][2],lx[M],rx[M],mx[M],sum[M];
int l,r,val[M],siz[M],par[M],fl[M],la[M];
char s[15];
struct Memory_Pool//内存池
{
int top,p[M];
Memory_Pool()
{
top=M-1;
for(int i=0;i<M;i++)
p[i]=i;
}
int New() {return p[top--];}
void Rec(int x) {p[++top]=x;}
}P;
int get_node(int v)
{
int t=P.New();
ch[t][0]=ch[t][1]=par[t]=0;
siz[t]=1;lx[t]=rx[t]=max(0,v);
mx[t]=sum[t]=val[t]=v;fl[t]=0;la[t]=inf;
return t;
}
void push_up(int x)
{
if(!x) return ;
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
lx[x]=max(lx[ch[x][0]],sum[ch[x][0]]+val[x]+lx[ch[x][1]]);
rx[x]=max(rx[ch[x][1]],sum[ch[x][1]]+val[x]+rx[ch[x][0]]);
if(x==M-1 || x==M-2) val[x]=-inf;//把哨兵当做中转点,不考虑他中间的计算
mx[x]=max(max(mx[ch[x][0]],mx[ch[x][1]]),rx[ch[x][0]]+val[x]+lx[ch[x][1]]);
if(x==M-1 || x==M-2) val[x]=0;//还是要赋回来
}
void modify(int x,int v)
{
if(!x) return ;
val[x]=v;sum[x]=siz[x]*v;la[x]=v;
if(v>0) lx[x]=rx[x]=mx[x]=sum[x];
else lx[x]=rx[x]=0,mx[x]=val[x];
}
void flip(int x)
{
if(!x) return ;
swap(ch[x][0],ch[x][1]);
swap(lx[x],rx[x]);
fl[x]^=1;
}
void push_down(int x)
{
if(!x) return ;
if(la[x]!=inf)
{
modify(ch[x][0],la[x]);
modify(ch[x][1],la[x]);
la[x]=inf;
}
if(fl[x])
{
flip(ch[x][0]);flip(ch[x][1]);
fl[x]=0;
}
}
int chk(int x)
{
return ch[par[x]][1]==x;
}
void rotate(int x)
{
int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
push_down(y);push_down(x);
ch[y][k]=w;par[w]=y;
ch[z][chk(y)]=x;par[x]=z;
ch[x][k^1]=y;par[y]=x;
push_up(y);push_up(x);
}
void splay(int x,int goal=0)
{
while(par[x]^goal)
{
int y=par[x],z=par[y];
if(z!=goal)
{
if(chk(x)==chk(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if(!goal) rt=x;
}
int find(int k)
{
int cur=rt;
while(1)
{
push_down(cur);
if(ch[cur][0] && k<=siz[ch[cur][0]])
cur=ch[cur][0];
else if(ch[cur][1] && k>siz[ch[cur][0]]+1)
{
k-=siz[ch[cur][0]]+1;
cur=ch[cur][1];
}
else return cur;
}
}
void ins(int x,int k)
{
int a=find(k),b=find(k+1);
splay(a);splay(b,a);
ch[b][0]=x;par[x]=b;
push_up(b);
splay(b);
}
void clear(int x)
{
if(!x) return ;
clear(ch[x][0]);
clear(ch[x][1]);
ch[x][0]=ch[x][1]=par[x]=0;
P.Rec(x);
}
void del(int l,int r)//删除[l,r](不包括哨兵)
{
int a=find(l),b=find(r+2);
splay(a);splay(b,a);
clear(ch[b][0]);
ch[b][0]=0;
push_up(b);
splay(b);
}
void work(int x)
{
if(!x) return ;
work(ch[x][0]);
work(ch[x][1]);
push_up(x);
}
void print(int x)
{
if(!x) return ;
push_down(x);
print(ch[x][0]);
printf("%d ",x);
print(ch[x][1]);
}
int main()
{
mx[0]=-inf;
n=read();m=read();
l=rt=P.New();r=P.New();la[l]=la[r]=inf;
ch[l][1]=r;par[r]=l;siz[r]=1;siz[l]=2;
for(int i=1;i<=n;i++)
{
int t=get_node(read());
ins(t,i);
}
while(m--)
{
scanf("%s",s);
if(s[0]=='I')
{
l=read();r=read();
for(int i=1;i<=r;i++)
a[i]=get_node(read());
for(int i=2;i<=r;i++)
{
ch[a[i]][0]=a[i-1];
par[a[i-1]]=a[i];
}//ch和par都要赋值
work(a[r]);
ins(a[r],l+1);
}
if(s[0]=='D')
{
l=read();r=read()+l-1;
del(l,r);
}
if(s[0]=='M' && s[2]=='K')
{
l=read(),r=read()+l-1;
int a=find(l),b=find(r+2);
splay(a);splay(b,a);
modify(ch[b][0],read());
splay(ch[b][0]);
}
if(s[0]=='R')
{
l=read(),r=read()+l-1;
int a=find(l),b=find(r+2);
splay(a);splay(b,a);
flip(ch[b][0]);
splay(ch[b][0]);
}
if(s[0]=='G')
{
l=read(),r=read()+l-1;
int a=find(l),b=find(r+2);
splay(a);splay(b,a);
printf("%d\n",sum[ch[b][0]]);
splay(b);
}
if(s[0]=='M' && s[2]=='X')
{
printf("%d\n",mx[rt]);
}
}
}