123789456ye

已AFO

[NOI 2005]维护数列

[NOI 2005]维护数列

题意:
题意
题解:
平衡树区间操作大礼包
以下是 fhqTreap 的
关于 fhq 的原理可以看(理解 split 和 merge 时自己多画点图)这个
fhq的区间操作很简单,和线段树是一样的,都是维护一些\(tag\)
如果你要对\([l,r]\)区间加
你就先\(split(root,l,x,y)\),再\(split(y,r-l+1,y,z)\)
再对\(y\)打一个\(tag_sum\),标记下传即可
回到这题
最大子段和用分治做,维护\(lmax,rmax,mmax\)表示从最左端起的最大子段和,最右端起的,整段的
转移看代码,很容易理解
复杂度:\(O(n\log n)\)
听说splay常数比较小,可是splay太长不想打
其实是我不会打

#include<bits/stdc++.h>
using namespace std;
#define maxn 500005
inline void read(int& x)
{
	x=0;char c=getchar();int f=1;
	while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	x*=f;
}
int sta[maxn];
queue<int> rubbish;//垃圾回收
namespace fhqTreap
{
#define ls son[rt][0]
#define rs son[rt][1]
#define inf 0x3f3f3f3f
	int son[maxn][2],siz[maxn],val[maxn],rnd[maxn],sum[maxn];//sum是区间和
	int lmax[maxn],rmax[maxn],mmax[maxn],cov[maxn];//cov是区间覆盖的tag
	bool lazy_reverse[maxn];//是否翻转tag
	int root,tot,x,y,z;
	void del(const int& rt)//删除掉这颗子树
	{
		if(ls) del(ls);
		rubbish.push(rt);
		if(rs) del(rs);
	}
	inline int newnode(int v)//新建节点
	{
		int rt;
		if(rubbish.empty()) rt=++tot;
		else rt=rubbish.front(),rubbish.pop();//如果垃圾桶里还有就用
		siz[rt]=1;rnd[rt]=rand();
		val[rt]=sum[rt]=lmax[rt]=mmax[rt]=rmax[rt]=v;
		ls=rs=0;
		cov[rt]=inf,lazy_reverse[rt]=0;
		return rt;
	}
	inline void cover_reverse(int rt,int flag,int v)
	//覆盖优先级大于翻转(因为你覆盖完了就没有翻转的必要了)
	{
		if(v!=inf)//cover
		{
			val[rt]=v,sum[rt]=v*siz[rt];
			lmax[rt]=mmax[rt]=rmax[rt]=(v>0?sum[rt]:val[rt]);
			cov[rt]=v;
			flag=0;
		}
		if(flag)//reverse
		{
			lazy_reverse[rt]^=1;
			swap(lmax[rt],rmax[rt]);
		}
	}
	inline void pushup(int& rt)//上传更新父节点数据
	{
		if(!rt) return;
		siz[rt]=1;
		mmax[rt]=lmax[rt]=rmax[rt]=sum[rt]=val[rt];
		if(ls)
		{
			mmax[rt]=max(max(mmax[rt],mmax[ls]),rmax[ls]+lmax[rt]);//子段和转移
			lmax[rt]=max(lmax[ls],sum[ls]+lmax[rt]);
			rmax[rt]=max(rmax[rt],rmax[ls]+sum[rt]);
			sum[rt]+=sum[ls],siz[rt]+=siz[ls];
		}
		if(rs)
		{
			mmax[rt]=max(max(mmax[rt],mmax[rs]),lmax[rs]+rmax[rt]);
			rmax[rt]=max(rmax[rs],sum[rs]+rmax[rt]);
			lmax[rt]=max(lmax[rt],sum[rt]+lmax[rs]);
			sum[rt]+=sum[rs],siz[rt]+=siz[rs];
		}
	}
	inline void pushdown(int rt)//标记下传
	{
#define rev lazy_reverse[rt]
		if((!rev&&cov[rt]==inf)||!rt) return;
		if(ls) cover_reverse(ls,rev,cov[rt]);
		if(rs) cover_reverse(rs,rev,cov[rt]);
		if(rev) swap(ls,rs);
		rev=0,cov[rt]=inf;
#undef rev
	}
	void split(int rt,int k,int& x,int& y)//注意区间问题必须用rank分裂
	{
		if(!rt) x=y=0;
		else
		{
			pushdown(rt);
			if(k>siz[ls]) x=rt,split(rs,k-siz[ls]-1,rs,y);
			else y=rt,split(ls,k,x,ls);
			pushup(rt);
		}
	}
	int merge(int x,int y)//注意如果你把0给上传或者下传了会发生一些奇怪的错误(我是RE)
	{
		if(!x&&!y) return 0;
		if(!x) {pushdown(y);return y;}
		if(!y) {pushdown(x);return x;}
		pushdown(x),pushdown(y);
		if(rnd[x]<rnd[y])
		{
			son[x][1]=merge(son[x][1],y);
			pushup(x);
			return x;
		}
		else
		{
			son[y][0]=merge(x,son[y][0]);
			pushup(y);
			return y;
		}
	}
	inline int build(int a[],const int& l,const int& r)//笛卡尔树O(n)建树的方法
	{
		int top=0;
		for(int i=l;i<=r;++i) a[i]=newnode(a[i]);
		for(int i=l;i<=r;++i)
		{
			while(top&&rnd[a[i]]<rnd[sta[top]]) 
			    pushup(sta[top]),son[a[i]][0]=sta[top--];
			if(top) son[sta[top]][1]=a[i];
			sta[++top]=a[i];
		}
		while(top) pushup(sta[top--]);
		return sta[1];
	}
	inline void insert(int a[],int pos,int len)
	//连续插入一段数,先建好在直接merge进去。直接一个个插会TLE
	{
		y=build(a,1,len);
		split(root,pos,x,z);
		root=merge(merge(x,y),z);
	}
	inline void del(int pos,int len)//删除区间
	{
		split(root,pos-1,x,y);
		split(y,len,y,z);
		del(y);
		root=merge(x,z);
	}
	inline void cover(int pos,int len,int v)//区间覆盖
	{
		split(root,pos-1,x,y);
		split(y,len,y,z);
		cover_reverse(y,0,v);
		root=merge(merge(x,y),z);
	}
	inline void reverse(int pos,int len)//区间翻转
	{
		split(root,pos-1,x,y);
		split(y,len,y,z);
		cover_reverse(y,1,inf);
		root=merge(merge(x,y),z);
	}
	inline int getsum(int pos,int len)//区间和
	{
		split(root,pos-1,x,y);
		split(y,len,y,z);
		int ans=sum[y];
		root=merge(merge(x,y),z);
		return ans;
	}
	inline int getmaxsum()//最大子段和
	{
		return mmax[root];
	}
	
	void print(int rt)//输出整棵树
	{
		if(ls) print(ls);
		printf("%d ",val[rt]);
		if(rs) print(rs);
	}
}

using namespace fhqTreap;
int a[maxn];
int main()
{
	//freopen("test.in","r",stdin);
	srand(time(NULL));
	int n,m,len,pos;
	read(n),read(m);
	for(int i=1;i<=n;++i) read(a[i]);
	root=build(a,1,n);
	//print(root);puts(" ");
	char op[10];
	while(m--)
	{
		scanf("%s",op);
		if(op[0]=='M'&&op[5]=='U')
		{
			printf("%d\n",getmaxsum());
			continue;
		}
		read(pos),read(len);
		if(op[0]=='I')
		{
			for(int i=1;i<=len;++i) read(a[i]);
			insert(a,pos,len);
		}
		else if(op[0]=='D') del(pos,len);
		else if(op[0]=='M') read(a[1]),cover(pos,len,a[1]);
		else if(op[0]=='R') reverse(pos,len);
		else if(op[0]=='G')	printf("%d\n",getsum(pos,len));
		//print(root);puts(" ");
	}
	return 0;
}

另外,如果你只想维护序列而不需要排序,你可以

inline void insert(int v)
{
    root=merge(root,newnode(v));
}

然后\(print(root)\)就能得到原序列

posted @ 2020-01-14 17:29  123789456ye  阅读(126)  评论(0编辑  收藏  举报