博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

洛谷.2042.[NOI2005]维护数列(Splay)

题目链接

2017.12.24 第一次写:
时间: 2316ms (1268ms)
空间: 19.42MB (19.5MB)(O2)
注:洛谷测的时间浮动比较大

/*
插入一段数:将这些数先单独建一棵平衡树t'.将pos旋到根,pos+1旋到son[root][1],直接将t'放到son[pos+1][0]即可 
删除一段数:将要删除区间旋转到r+1的左儿子上,直接删除 
统一修改一段数、区间反转:同样,提取修改区间,打标记 
区间求和:提取,输出记录的sum 
最大子序列:有一种求最大子序列的nlogn分治算法,用于带修改的最大子序列 
	就是记录lmax,rmax,maxn,sum(是否包含区间左/右端点),dp思想更新 
1.最多会出现4*10^6个数,任意时刻只会有5*10^5个数,所以要用一个栈或队列存储可用下标(内存池) 
  要id[]记录 
  有Delete掉的下标就存;栈里没有就++cnt 
  Build时一定要分清是位置m还是下标rt 
2.sum[ls],sum[rs]都是不包括val[rt]的,转移时注意加上 
3.下传rt节点时是去用标记更新ls,rs,而不是还更新rt。没理解透。
  更新rt的标记时要更新rt各值!包括rev。在下传时给子节点标记同时更新子节点 
  也许不这样会导致标记下传不完全,因为这题是多询问的,而文艺平衡树是在最后输出,输出时一定会更新标记。 
4.区间反转会导致lmx,rmx反了,要换!
5.Down()一般只需要在Find向下递归时进行就可以 
6.由于每次Split都会导致父子关系改变,所以每次更新都要Update(son[root]),Update(root) 
7.初始化mx[0],这样更新最值可以不用管是否存在子节点 
8.mx和lmx,rmx更新方式是不一样的! 
注意转移 
*/
#include<cstdio>
#include<cctype>
#include<algorithm>
#define gc() getchar()
const int N=5e5+5,INF=1e9;

int n,root,A[N],id[N],sk[N],tp,cnt;
int fa[N],son[N][2],sz[N],val[N],lmx[N],rmx[N],mx[N],sum[N];
bool tag[N],rev[N];//直接用val[rt]的值代替int tag[rt] 
inline int read()
{
	int now=0,f=1;register char c=gc();
	for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now*f;
}
#define ls son[rt][0]
#define rs son[rt][1]
inline void Update(int rt)
{
	sz[rt]=sz[ls]+sz[rs]+1;
	mx[rt]=std::max(mx[ls],std::max(mx[rs],rmx[ls]+val[rt]+lmx[rs]));
	lmx[rt]=std::max(lmx[ls],sum[ls]+val[rt]+lmx[rs]);
	rmx[rt]=std::max(rmx[rs],sum[rs]+val[rt]+rmx[ls]);
	sum[rt]=sum[ls]+val[rt]+sum[rs];
}
inline void Down(int rt)
{
	if(tag[rt])
	{
		if(ls)
		{
			tag[ls]=1, val[ls]=val[rt], sum[ls]=sz[ls]*val[rt];
			if(val[rt]>=0) lmx[ls]=rmx[ls]=mx[ls]=sum[ls];
			else lmx[ls]=rmx[ls]=0,mx[ls]=val[rt];
		}
		if(rs)
		{
			tag[rs]=1, val[rs]=val[rt], sum[rs]=sz[rs]*val[rt];
			if(val[rt]>=0) lmx[rs]=rmx[rs]=mx[rs]=sum[rs];
			else lmx[rs]=rmx[rs]=0,mx[rs]=val[rt];
		}
		rev[rt]=tag[rt]=0;//有修改标记就不需要反转 
	}
	else if(rev[rt])
	{
		rev[rt]=0, rev[ls]^=1, rev[rs]^=1;
		std::swap(lmx[ls],rmx[ls]), std::swap(son[ls][0],son[ls][1]);
		std::swap(lmx[rs],rmx[rs]), std::swap(son[rs][0],son[rs][1]);
//	WA:	std::swap(lmx[rt],rmx[rt]);
//		std::swap(ls,rs);
	}
}
void Rotate(int x,int &k)
{
	int a=fa[x],b=fa[a];bool l=son[a][1]==x,r=l^1;
	if(a==k) k=x;
	else son[b][son[b][1]==a]=x;
	fa[x]=b, fa[a]=x, fa[son[x][r]]=a;
	son[a][l]=son[x][r], son[x][r]=a;
	Update(a),Update(x);
}
void Splay(int x,int &k)
{
	while(x!=k)
	{
		int a=fa[x],b=fa[a];
		if(a!=k) (son[a][1]==x^son[b][1]==a)?Rotate(x,k):Rotate(a,k);
		Rotate(x,k);
	}
}
int Rank(int rt,int k)
{
	while(1)
	{
		Down(rt);
		if(sz[ls]+1==k) return rt;
		if(sz[ls]>=k) rt=ls;
		else k-=sz[ls]+1,rt=rs;
	}
}
void Build(int l,int r,int f)
{
	if(l>r) return;
	int m=l+r>>1,rt=id[m],pre=id[f];
	fa[rt]=pre, son[pre][m>f]=rt;/*,sz[rt]=1;*///son[pre][m>f]:注意数组下标和位置没有关系 
	rev[rt]=tag[rt]=0, val[rt]=A[m];//因为sum,lmx等还会在Update重新更新 
	if(l==r)//所以只在这时更新这些值 
	{
		mx[rt]=sum[rt]=A[l], sz[rt]=1;//mx[rt]是这个区间(点)必须要选,所以就是A[l] 
		lmx[rt]=rmx[rt]=std::max(val[rt],0);
		return;
	}
	Build(l,m-1,m), Build(m+1,r,m);
	Update(rt);
}
void Free(int rt)//时间换空间,回收无用编号 
{
	if(!rt) return;
	sk[++tp]=rt;
	Free(ls), Free(rs);
	fa[rt]=ls=rs=0;
//	rev[rt]=tag[rt]=0;
}
int Split(int pos,int tot)
{
	int x=Rank(root,pos),y=Rank(root,pos+tot+1);
	Splay(x,root), Splay(y,son[x][1]);
	return son[y][0];
}
void Ins()
{
	int pos=read()+1,tot=read();
	for(int i=1;i<=tot;++i) A[i]=read();
	for(int i=1;i<=tot;++i)
		if(tp) id[i]=sk[tp--];
		else id[i]=++cnt;
	Build(1,tot,0);
	int z=id[1+tot>>1],x=Rank(root,pos),y=Rank(root,pos+1);//z:the root of t'
	Splay(x,root), Splay(y,son[x][1]);
	fa[z]=y, son[y][0]=z;
	Update(y), Update(x);
}
void Del()
{
	int pos=read(),tot=read(),y=Split(pos,tot),x=fa[y];
	Free(y), son[x][0]=0;
	Update(x), Update(fa[x]);//不要再用y! 
}
void Modify()
{
	int pos=read(),tot=read(),v=read(),rt=Split(pos,tot);
	tag[rt]=1,val[rt]=v,sum[rt]=sz[rt]*v;
	if(v>=0) lmx[rt]=rmx[rt]=sum[rt], mx[rt]=sum[rt];//lmx,rmx全选或不选 
	else lmx[rt]=rmx[rt]=0, mx[rt]=v;//mx全选或选一个 
	Update(fa[rt]), Update(root);
}
void Rev()
{
	int pos=read(),tot=read(),rt=Split(pos,tot);
	if(!tag[rt])
	{
		rev[rt]^=1;
		std::swap(ls,rs), std::swap(lmx[rt],rmx[rt]);
		Update(fa[rt]), Update(root);
	}
//WA:rev[rt]^=1;
//	Update(fa[rt]), Update(root);
}
void Query()
{
	int pos=read(),tot=read(),rt=Split(pos,tot);
	printf("%d\n",sum[rt]);
}
//void Print(int x)
//{
//	if(!x) return;
//	Down(x);
//	Print(son[x][0]), printf("%d ",val[x]), Print(son[x][1]);
//}

int main()
{
//#ifndef ONLINE_JUDGE
//	freopen("2042.in","r",stdin);
//#endif

//	freopen("seq2005.in","r",stdin);
//	freopen("seq2005.out","w",stdout);

	n=read();int q=read();
	mx[0]=A[1]=A[n+2]=-INF;
	for(int i=1;i<=n;++i) A[i+1]=read(),id[i]=i;
	id[n+1]=n+1,id[n+2]=n+2;
	Build(1,n+2,0), root=n+3>>1, cnt=n+2;
	char s[12];
	while(q--)
	{
		scanf("%s",s);
		if(s[0]=='I') Ins();
		else if(s[0]=='D') Del();
		else if(s[2]=='K') Modify();
		else if(s[0]=='R') Rev();
		else if(s[0]=='G') Query();
		else printf("%d\n",mx[root]);
	}

	return 0;
}

2018.2.26 第二次写:(简化许多)(两份实际行数倒差不多)
时间: 2008ms (1536ms)
空间:19.51MB (19.49MB)

/*
数列位置直接拿sz解决 
需要存个id,下标并不统一 
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=5e5+15,INF=1e8,MAXIN=1e6;

int n,m,root;
inline int read()
{
	int now=0,f=1;register char c=gc();
	for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now*f;
}
struct SPLAY
{
	int A[N],size,fa[N],son[N][2],val[N],sz[N],id[N],sum[N],lmax[N],rmax[N],smax[N];
	int pool[N],top,cnt;
	bool cover[N],rev[N];
	inline int New_Node(){
		if(top) return pool[--top];
		return ++cnt;
	}
	inline void Update(int rt)
	{
		int ls=son[rt][0],rs=son[rt][1];
		sz[rt]=sz[ls]+sz[rs]+1;
		sum[rt]=sum[ls]+val[rt]+sum[rs];
		smax[rt]=std::max(rmax[ls]+val[rt]+lmax[rs],std::max(smax[ls],smax[rs]));
		lmax[rt]=std::max(lmax[ls],sum[ls]+val[rt]+lmax[rs]);
		rmax[rt]=std::max(rmax[rs],sum[rs]+val[rt]+rmax[ls]);
	}
	inline void Set(int rt,int v)
	{
		sum[rt]=v*sz[rt], val[rt]=v, cover[rt]=1;
		if(v>0) smax[rt]=lmax[rt]=rmax[rt]=sum[rt];
		else smax[rt]=v, lmax[rt]=rmax[rt]=0;//至少存在一元素 但是作为区间端点还是可以不要的 
	}
	inline void Rev(int rt)
	{
		std::swap(lmax[rt],rmax[rt]), std::swap(son[rt][0],son[rt][1]);
		rev[rt]^=1;
	}
	void PushDown(int rt)
	{
		if(cover[rt])
			Set(son[rt][0],val[rt]), Set(son[rt][1],val[rt]), cover[rt]=rev[rt]=0;//这个rev没有必要了 
		if(rev[rt])
			Rev(son[rt][0]), Rev(son[rt][1]), rev[rt]^=1;
	}
	void Rotate(int x,int &k)
	{
		int a=fa[x],b=fa[a]; bool l=son[a][1]==x,r=l^1;
		if(k==a) k=x;
		else son[b][son[b][1]==a]=x;
		fa[a]=x, fa[x]=b, fa[son[x][r]]=a,
		son[a][l]=son[x][r], son[x][r]=a;
		Update(a), Update(x);
	}
	void Splay(int x,int &k)
	{
		while(x!=k)
		{
			int a=fa[x],b=fa[a];
//			PushDown(b), PushDown(a), PushDown(x);
			if(a!=k) son[a][1]==x^son[b][1]==a?Rotate(x,k):Rotate(a,k);
			Rotate(x,k);
		}
	}
	int Rank(int k,int p)
	{
		while(1)
		{
			PushDown(k);
			if(sz[son[k][0]]+1==p) return k;
			if(sz[son[k][0]]>=p) k=son[k][0];
			else p-=sz[son[k][0]]+1, k=son[k][1];
		}
	}
	void Build(int l,int r,int f)
	{
		if(l>r) return;
		int m=l+r>>1,rt=id[m],pre=id[f];
		val[rt]=A[m], fa[rt]=pre, son[pre][m>f]=rt;//son[pre][val[rt]>val[pre]]=rt;同样的错误--在树中的排名是下标!
		cover[rt]=rev[rt]=0;
		if(l==r){
			sz[rt]=1, sum[rt]=smax[rt]=val[rt];
			if(val[rt]>0) lmax[rt]=rmax[rt]=val[rt];
			else lmax[rt]=rmax[rt]=0;
		}
		else{//其余信息在Update时更新 
			Build(l,m-1,m), Build(m+1,r,m),
			Update(rt);
		}
	}
	void Insert()
	{
		int p=read()+1,tot=read();
		for(int i=1; i<=tot; ++i) A[i]=read(),id[i]=New_Node();
		Build(1,tot,0);
		int r=id[1+tot>>1],x=Rank(root,p),y=Rank(root,p+1);
		Splay(x,root), Splay(y,son[x][1]);
		fa[r]=y, son[y][0]=r;
		Update(y), Update(x);
	}
	void Free(int x)
	{
		pool[top++]=x;
		if(son[x][0]) Free(son[x][0]);
		if(son[x][1]) Free(son[x][1]);
		son[x][0]=son[x][1]=0;//son不会在Build时更新 
//		fa[x]=rev[x]=cover[x]=0;
	}
	int Split()
	{
		int p=read(),tot=read();
		int x=Rank(root,p),y=Rank(root,p+tot+1);
		Splay(x,root), Splay(y,son[x][1]);
//		printf("p:%d tot:%d x:%d y:%d z:%d\n",p,tot,x,y,son[y][0]);
		return son[y][0];
	}
	void Delete()
	{
		int z=Split(),y=fa[z];
		/*if(z)*/ Free(z);//当前z这里不需要特判 不会删空元素 
		son[y][0]=0, Update(y), Update(fa[y]);
	}
	void Modify(){
		int z=Split(),v=read();
		Set(z,v), Update(fa[z]), Update(root);//更新!
	}
	void Reverse(){
		int rt=Split();
		if(!cover[rt])//在无cover标记时反转 
			Rev(rt), Update(fa[rt]), Update(root);
	}
	int Query_Sum(){
		printf("%d\n",sum[Split()]);
	}
//	void Print(int x)
//	{
//		PushDown(x);
//		if(son[x][0]) Print(son[x][0]);
//		printf("%d:%d\n",x,val[x]);
//		if(son[x][1]) Print(son[x][1]);
//	}
//	void Debug()
//	{
//		puts("Debug");
//		Print(root);
//		putchar('\n');
//	}
}t;


int main()
{
	n=read(),m=read();
	t.id[1]=1, t.id[n+2]=n+2;//不能用0 
	t.A[1]=t.A[n+2]=-INF;//边界两个点要处理不能算答案!
	for(int i=2; i<=n+1; ++i) t.A[i]=read(),t.id[i]=i;
	t.cnt=n+2, t.Build(1,n+2,0);
	root=t.id[n+3>>1];
	char opt[15];
	while(m--)
	{
		scanf("%s",opt);
		if(opt[0]=='I') t.Insert();
		else if(opt[0]=='D') t.Delete();
		else if(opt[4]=='-') t.Modify();
		else if(opt[0]=='R') t.Reverse();
		else if(opt[0]=='G') t.Query_Sum();
		else printf("%d\n",t.smax[root]);
	}
	return 0;
}
posted @ 2018-02-08 16:00  SovietPower  阅读(165)  评论(0编辑  收藏  举报