Loading

(板子)平衡树维护序列

爆改了之前文艺平衡树的板子。

luoguP2710 数列

大致题意:
维护数列,支持 插入、删除、区间翻转、区间覆盖、区间求和、区间最大子段和。

code: (头文件等略)

const int maxn=200010,inf=2147483647;
int n,m,tot,rt;
int reuse[maxn],top;//这题有删除,所以搞了个结点回收(尝试用手写栈卡常)
struct node
{
	int l,r,val,key,siz;//平衡树的基本信息
	int cov,rev;//标记
	int sum,lans,rans,ans;//维护的答案
	//ans维护最后的区间最大子段和,可能为负;但lans和rans是可以不取的,最小为0
}tree[maxn];
inline void newnode(int &x,int val)
{
	if(top)x=reuse[top--];//如果栈里有可用的结点,就先用栈里的
	else x=++tot;
	tree[x]=(node)
	{
		0,0,val,rand(),1,\
		inf,0,\
		val,max(val,0ll),max(val,0ll),val\
	};//与上面node结构体里的对应起来
}
inline void pushup(int x)//可以参考GSS3. 注意平衡树是将左子树,右子树和结点三部分合起来,并且可能有左右子树不存在的情况
{
	int lson=tree[x].l,rson=tree[x].r;
	tree[x].siz=tree[lson].siz+tree[rson].siz+1;
	tree[x].sum=tree[lson].sum+tree[rson].sum+tree[x].val;
	tree[x].lans=max(tree[lson].lans,max(0ll,tree[lson].sum+tree[x].val+tree[rson].lans));
	tree[x].rans=max(tree[rson].rans,max(0ll,tree[rson].sum+tree[x].val+tree[lson].rans));
	tree[x].ans=tree[lson].rans+tree[rson].lans+tree[x].val;
	if(lson)tree[x].ans=max(tree[x].ans,tree[lson].ans);
	if(rson)tree[x].ans=max(tree[x].ans,tree[rson].ans);
}
inline void pushrev(int x)
{
	swap(tree[x].l,tree[x].r);
	swap(tree[x].lans,tree[x].rans);//别忘了把这个也给翻转一下
	tree[x].rev^=1;
}
inline void pushcover(int x,int k)
{
	tree[x].val=tree[x].cov=k;
	tree[x].sum=tree[x].siz*k;
	tree[x].lans=tree[x].rans=max(0ll,tree[x].sum);
	tree[x].ans=max(k,tree[x].sum);//防止k为负
}
inline void pushdown(int x)
{
	if(tree[x].cov!=inf)
	{
		if(tree[x].l)pushcover(tree[x].l,tree[x].cov);
		if(tree[x].r)pushcover(tree[x].r,tree[x].cov);
		tree[x].cov=inf;
	}
	if(tree[x].rev)
	{
		if(tree[x].l)pushrev(tree[x].l);
		if(tree[x].r)pushrev(tree[x].r);
		tree[x].rev=0;
	}
}
inline void split(int x,int siz,int &rx,int &ry)
{
	if(!x){rx=ry=0;return;}
	pushdown(x);
	if(tree[tree[x].l].siz<siz)
	{
		rx=x;
		split(tree[x].r,siz-tree[tree[x].l].siz-1,tree[x].r,ry);
	}
	else
	{
		ry=x;
		split(tree[x].l,siz,rx,tree[x].l);
	}
	pushup(x);
}
inline int merge(int x,int y)
{
	pushdown(x);pushdown(y);
	if(!x||!y)return x+y;
	if(tree[x].key<tree[y].key)
	{
		tree[x].r=merge(tree[x].r,y);
		pushup(x);return x;
	}
	else
	{
		tree[y].l=merge(x,tree[y].l);
		pushup(y);return y;
	}
}
int build(int l,int r)//一次插入多个结点用类似线段树建树的方式来优化复杂度
//要不然会被NOI那道维护数列的离谱插入次数卡爆
{
	if(l==r)
	{
		int now,val;cin >> val;
		newnode(now,val);
		return now;
	}
	int mid=(l+r)>>1;
	int x=build(l,mid),y=build(mid+1,r);
	return merge(x,y);
}
void recycle(int x)//结点回收
{
	reuse.push(x);
	if(tree[x].l)recycle(tree[x].l);
	if(tree[x].r)recycle(tree[x].r);
}
inline void del(int l,int r)//区间删除不需要一个一个删
{
	int x,y,z;
	split(rt,l-1,x,y);
	split(y,r-l+1,y,z);
	recycle(y);//但是结点回收还是会把整个区间遍历一遍。
	rt=merge(x,z);
}
inline void modify_rev(int l,int r)
{
	int x,y,z;
	split(rt,l-1,x,y);
	split(y,r-l+1,y,z);
	pushrev(y);
	rt=merge(merge(x,y),z);
}
inline void modify_cov(int l,int r,int k)
{
	int x,y,z;
	split(rt,l-1,x,y);
	split(y,r-l+1,y,z);
	pushcover(y,k);
	rt=merge(merge(x,y),z);
}
inline int query_sum(int l,int r)
{
	int x,y,z;
	split(rt,l-1,x,y);
	split(y,r-l+1,y,z);
	int now=tree[y].sum;
	rt=merge(merge(x,y),z);
	return now;
}
inline int query_ans(int l,int r)
{
	int x,y,z;
	split(rt,l-1,x,y);
	split(y,r-l+1,y,z);
	int now=tree[y].ans;
	rt=merge(merge(x,y),z);
	return now;
}

注意build函数的用法:
一开始读入数组中的n个数直接 rt=build(1,n); 即可
在pos之后连续插入t个数应该这样写:

int x,y;split(rt,pos,x,y);
rt=merge(merge(x,build(pos,pos+t-1)),y);
posted @ 2023-03-02 19:32  pjykk  阅读(40)  评论(0编辑  收藏  举报