维护数列

一、题目

点此看题

二、解法

写一发无旋 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]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值