【SPLAY】SPOJ1470维护数列

       BZOJ跪了我找了好多OJ才又找到维护数列这个题~话说SPOJ好像比较慢的样子~我找别人以前在BZOJ上AC的程序在SPOJ上竟然TLE了~加了读入优化还是TLE~囧~这算个什么事~今天把splay的数组实现改成了指针实现以后终于AC掉了~完全不知道我的splay哪里写慢了QAQ

       splay总体来说就是转来转去转来转去【转晕了诶】各种操作都是在旋转的基础上完成的。splay因为不需要维护树的严格平衡,可以实现很多其它平衡树不能完成的操作。总体来说splay的功能有如下一些:

       单点插入、查询、删除(所有平衡树都可以)

       单点、成段更新(其它平衡树不能、线段树和splay可以)(呃~好像其它平衡树可以单点更新的样子呢喵)

       成段插入、删除、区间翻转(其它平衡树和线段树都不能,只有splay可以)

       也就是说,splay可以代替线段树,但是和线段树比起来代码量过大了QAQ

对区间的操作方式是:把区间左端点的左边一位转到根,右端点的右边一位转到根的右子树,这样根的右子树的左子树就是要操作的区间。开始建树时要在树上建两-INF的结点保证区间的两个端点一定存在。

2012-12-26

关于splay的双旋转,要这样旋转的原因是为了维护splay的相对平衡,如果总是转x结点一直转到根那么一条链转完以后还是一条链,而使用双旋转则可以使旋转后的splay变得相对平衡。这一部分在杨思雨的论文里有详细的图解。

更具体一些的可以围观Crash神的论文

囧~话说平衡树我是先学的SBT,没学splay是因为我当时不会线段树,本来是要交给蛋蛋去学的~然后我之所以不会线段树是因为觉得他俩都会了我就不学了QAQ然后~我还是学了~学完以后发现这玩意好简单……然后开始折腾splay【是这货折腾我】……理论到是不难~写起来还真是各种纠结~围观了Crash神的论文和各位神的代码终于搞明白了OTL

下面送上我的splay模板~个人觉得写得还是很清晰的~

/*
    插入:INSERT pos n c1...cn (pos后面插入n个数字)
    删除:DELETE pos n (删除pos开始的n个数字)
    修改:MAKE-SAME pos n c (pos开始的n个数字修改为c)
    翻转:REVERSE pos n (pos开始的n个数字翻转)
    求和:GET-SUM pos n (pos开始的n个数字求和)
    求和最大的子序列:MAX-SUM (整个区间的和最大的子序列)
*/
#include <cstdio>
#include <iostream>
using namespace std;
#define INF 1<<30
#define N 500010
int n,m;
int num[N];
struct node
{
	node *ch[2],*pre;
	int sz,val,sum,ls,rs,ss;
	bool lzy1;
	int lzy2;
};
struct SplayTree
{
	node *root,*null,*pool[N],data[N];
	int n,cnt,top;
	void rotate(node *x,int f)//旋转
	{
		node *y=x->pre,*z=y->pre;
		push_down(y);
		push_down(x);
		y->ch[!f]=x->ch[f];
		x->ch[f]->pre=y;
		x->ch[f]=y;
		y->pre=x;
		x->pre=z;
		if(x->pre!=null) z->ch[z->ch[1]==y]=x;
		push_up(y);
	}
	void splay(node *x,node *goal)//把x节点转到goal下面,goal为0表示转到根
	{
		push_down(x);
		while (x->pre!=goal)
		{
			if(x->pre->pre==goal) rotate(x,x->pre->ch[0]==x);
			else
			{
				node *y=x->pre,*z=y->pre;
				int f=(z->ch[0]==y);
				if(y->ch[f]==x) rotate(x,!f);
				else rotate(y,f);
				rotate(x,f);
			}
		}
		push_up(x);
		if(goal==null) root=x;
	}
	void rotateto(int k,node *goal)//把第k位转到goal下面
	{
		node *x=root;
		push_down(x);
		while (x->ch[0]->sz!=k)
		{
			if(k<x->ch[0]->sz) x=x->ch[0];
			else{
				k-=x->ch[0]->sz+1;
				x=x->ch[1];
			}
			push_down(x);
		}
		splay(x,goal);
	}
	//以上函数基本不用变
	void clear()
	{
		n=top=0;
		null=newnode(-INF);
		null->sz=null->sum=0;
		null->val=null->ls=null->rs=null->ss=-INF;
		null->lzy1=0;
		null->lzy2=-INF;
		root=newnode(-INF);
		root->ch[1]=newnode(-INF);
		root->ch[1]->pre=root;
		root->sz=2;
	}
	node *newnode(int c)
	{
		node *x;
		if (top) x=pool[top--];
		else x=&data[n++];
		x->ch[0]=x->ch[1]=x->pre=null;
		x->sz=1;
		x->val=x->ls=x->rs=x->ss=x->sum=c;
		x->lzy1=0;
		x->lzy2=-INF;
		return x;
	}
	void push_up(node *x)
	{
		node *lx=x->ch[0],*rx=x->ch[1];
		
		x->sz=1+lx->sz+rx->sz;
		x->sum=x->val+lx->sum+rx->sum;
		
		x->ls=max(lx->ls,lx->sum+x->val+max(0,rx->ls));
		x->rs=max(rx->rs,rx->sum+x->val+max(0,lx->rs));
		
		x->ss=max(0,lx->rs)+x->val+max(0,rx->ls);
		x->ss=max(x->ss,max(lx->ss,rx->ss));
	}
	void update_rev(node *x)
	{
		if(x==null) return;
		swap(x->ch[0],x->ch[1]);
		swap(x->ls,x->rs);
		x->lzy1^=1;
	}
	void update_same(node *x,int v)
	{
		if(x==null) return;
		x->val=v;
		x->sum=v*x->sz;
		x->ss=x->ls=x->rs=max(v,v*x->sz);
		x->lzy2=v;
	}
	void push_down(node *x)
	{
		if(x->lzy1)
		{
			update_rev(x->ch[0]);
			update_rev(x->ch[1]);
			x->lzy1=0;
		}
		if(x->lzy2!=-INF)
		{
			update_same(x->ch[0],x->lzy2);
			update_same(x->ch[1],x->lzy2);
			x->lzy2=-INF;
		}
	}
	void init(int pos,int tot)
	{
		clear();
		cnt=tot;
		rotateto(pos,null);
		rotateto(pos+1,root);
		root->ch[1]->ch[0]=build(1,tot,root->ch[1]);
		push_up(root->ch[1]);
		push_up(root);
	}
	node *build(int l,int r,node *f)
	{
		if(l>r) return null;
		push_down(f);
		int mid=(l+r)>>1;
		node *x=newnode(num[mid]);
		x->ch[0]=build(l,mid-1,x);
		x->ch[1]=build(mid+1,r,x);
		x->pre=f;
		push_up(x);
		return x;
	}
	void ins(int pos,int len)
	{
		cnt+=len;
		rotateto(pos,null);
		rotateto(pos+1,root);
		root->ch[1]->ch[0]=build(1,len,root->ch[1]);
		push_up(root->ch[1]);
		push_up(root);
	}
	void erase(node *x)
	{
		if(x==null) return;
		pool[++top]=x;
		erase(x->ch[0]);
		erase(x->ch[1]);
	}
	void del(int l,int r)
	{
		rotateto(l-1,null);
		rotateto(r+1,root);
		node *key=root->ch[1]->ch[0];
		root->ch[1]->ch[0]=null;
		cnt-=key->sz;
		erase(key);
		push_up(root->ch[1]);
		push_up(root);
	}
	void change(int l,int r,int c)
	{
		rotateto(l-1,null);
		rotateto(r+1,root);
		node *key=root->ch[1]->ch[0];
		update_same(key,c);
		push_up(root->ch[1]);
		push_up(root);
	}
	void flip(int l,int r)
	{
		rotateto(l-1,null);
		rotateto(r+1,root);
		node *key=root->ch[1]->ch[0];
		update_rev(key);
	}
	int get_sum(int l,int r)
	{
		rotateto(l-1,null);
		rotateto(r+1,root);
		node *key=root->ch[1]->ch[0];
		return key->sum;
	}
	int max_sum()
	{
		rotateto(0,null);
		rotateto(cnt+1,root);
		node *key=root->ch[1]->ch[0];
		return key->ss;
	}
	void print(node *x)
	{
		if(x->ch[0]!=null) print(x->ch[0]);
		printf("%d ",x->val);
		if(x->ch[1]!=null) print(x->ch[1]);
	}
}spl;
int main ()
{
	 int tt;
	 int i,j,t;
	 int a,b,c;
	 char op[30];
	 scanf("%d",&tt);
	 while (tt--)
	 {
	 	scanf("%d%d",&n,&m);
	 	for(i=1;i<=n;i++) scanf("%d",&num[i]);
	 	spl.init(0,n);
	 	/*spl.print(spl.root);
	 	printf("\n");
	 	return 0;*/
	 	while (m--)
	 	{
	 		scanf("%s",op);
	 		if(op[0]=='I')
	 		{
	 			scanf("%d%d",&a,&n);
	 			for(i=1;i<=n;i++) scanf("%d",&num[i]);
	 			spl.ins(a,n);
	 		}else
	 		if(op[0]=='D')
	 		{
	 			scanf("%d%d",&a,&b);
	 			spl.del(a,a+b-1);
	 		}else
	 		if(op[0]=='M' && op[2]=='K')
	 		{
	 			scanf("%d%d%d",&a,&b,&c);
	 			spl.change(a,a+b-1,c);
	 		}else
	 		if(op[0]=='R')
	 		{
	 			scanf("%d%d",&a,&b);
	 			spl.flip(a,a+b-1);
	 		}else
	 		if(op[0]=='G')
	 		{
	 			scanf("%d%d",&a,&b);
	 			printf("%d\n",spl.get_sum(a,a+b-1));
	 		}
	 		else if(op[0]=='M') printf("%d\n",spl.max_sum());
	 	}
	 }
	
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值