感人至深,splay的代码量

A Simple Problem with Integers

线段树大法好 splay码量太长了。

但是为了早日掌握splay,还是作死的写了一下

http://poj.org/problem?id=3468

就是线段树区间操作,lazy标记。只不过用splay实现。

写完之后发现,以前认为线段树代码量长,现在~~~~~~~

----------------------------树状数组更短-------------------------------

对比一下即可:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
	long long l,r,sum,lazy;
}tree[800000+20];
long long n,q;
void build(long long i,long long l,long long r)
{
	tree[i].l=l;
	tree[i].r=r;
	tree[i].lazy=0;
	if(l==r)
	{
		scanf("%lld",&tree[i].sum);
		return ;
	}
	long long mid=(l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
	
}
void pushdown(long long i)
{
	if(!tree[i].lazy)return ;
	long long l=tree[i].l;
	long long r=tree[i].r;
	long long len=r-l+1;
	tree[i*2].lazy+=tree[i].lazy;
	tree[i*2+1].lazy+=tree[i].lazy;
	tree[i*2].sum+=tree[i].lazy*(len-len/2);
	tree[i*2+1].sum+=tree[i].lazy*(len/2);
	tree[i].lazy=0;
}
void add(long long i,long long a,long long b,long long x)
{
	long long l=tree[i].l;
	long long r=tree[i].r;
	if(l>=a&&r<=b)
	{
		tree[i].sum+=x*(r-l+1);
		tree[i].lazy+=x;
		return ;
	}
	pushdown(i);
	long long mid=(l+r)>>1;
	if(a<=mid)add(i*2,a,b,x);
	if(b>mid)add(i*2+1,a,b,x);
	tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
}
long long query(long long i,long long a,long long b)
{
	long long l=tree[i].l;
	long long r=tree[i].r;
	if(l>=a&&r<=b)return tree[i].sum;
	pushdown(i);
	long long ans=0;
	long long mid=(l+r)/2;
	if(a<=mid)ans+=query(i*2,a,b);
	if(b>mid)ans+=query(i*2+1,a,b);
	return ans;
}
int main()
{
	scanf("%lld",&n);
	build(1,1,n);
	scanf("%lld",&q);
	for(long long i=1;i<=q;i++)
	{
		long long f;
		scanf("%lld",&f);
		if(f==1)
		{
			long long a,b,x;
			scanf("%lld%lld%lld",&a,&b,&x);
			add(1,a,b,x);
		}
		else if(f==2)
		{
			long long a,b;
			scanf("%lld%lld",&a,&b);
			printf("%lld\n",query(1,a,b));
		}
	}
	return 0;
}

我写的结构体~

恩,挺长的。

但是

---------------------------------------------------------


---------------------------------------------------------

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define maxn 100000+20
/*
bug数据 
5 5
1 2 3 4 5
C 2 4 3
Q 4 5
(12)  
*/
using namespace std;
long long ncnt=0;
struct node
{
	node *f;
	node *ch[2];
	long long sum,lazy,size,id;
	//分析一下我觉得需要记录这棵子树sum,lazy,这棵子树的size;
	//这个点的id (以便查询) 按id大小建树 
	node(){
		f=ch[0]=ch[1]=NULL;
		sum=lazy=size=0;
	}
	void zero()
	{
		f=ch[0]=ch[1]=NULL;
		sum=lazy=size=0;
	}
}S[maxn];
node *root;
long long n,q;
void pushdown(node *u)
{
	if(!u->lazy||u==NULL)return ;
	if(u->ch[0]!=NULL)
	{
		u->ch[0]->lazy+=u->lazy;
		u->ch[0]->sum+=(u->ch[0]->size)*(u->lazy);
	}
	if(u->ch[1]!=NULL)
	{
		u->ch[1]->lazy+=u->lazy;
	    u->ch[1]->sum+=(u->ch[1]->size)*(u->lazy);
	}
	
	u->lazy=0; 
}
void rotate(node *u)//把u旋到根 
{
	node *f=u->f;
	if(f==NULL)return ;
	pushdown(f);
	pushdown(u);
	node *ff=f->f;
	long long d=u==f->ch[1];
	long long dd=0;
	if(ff!=NULL)dd=f==ff->ch[1];
	
	if(u->ch[d^1]!=NULL)u->ch[d^1]->f=f;
	f->ch[d]=u->ch[d^1]; 
	
	long long size=u->size;
	u->size=f->size;
	f->size=f->size-size;
	if(u->ch[d^1]!=NULL)f->size+=u->ch[d^1]->size;//交换更新
	
	
	
	long long sum=u->sum;
	u->sum=f->sum;
	f->sum=f->sum-sum;
	if(u->ch[d^1]!=NULL)f->sum+=u->ch[d^1]->sum; 
	
	u->ch[d^1]=f;
	f->f=u;
	
	if(ff!=NULL)ff->ch[dd]=u;
	u->f=ff;
}
void splay(node *u,node *p)
{
	pushdown(u);
	while(u->f!=p)
	{
		node *f=u->f;
		node *ff=f->f;
		if(ff==p)
		{
			rotate(u);
			break;
		}
		long long d=u==f->ch[1];
		long long dd=f==ff->ch[1];
		if(d==dd)rotate(f);
		else rotate(u);
		rotate(u);
	}
	if(p==NULL)root=u;
}
void insert(long long key,long long id)
{
	if(root==NULL)
	{
		root=&S[++ncnt];
		root->f=NULL;
		root->id=id;
		root->sum=key;
		root->size=1;
		root->lazy=0;
		root->ch[0]=root->ch[1]=NULL;
		return ;
	}
	node *u=root;
	node *y;
	while(1)
	{
		u->size+=1;
		u->sum+=key;
		
		if(id<u->id)
		{
			if(u->ch[0]!=NULL)u=u->ch[0];
			else
			{
				y=&S[++ncnt];
				y->id=id;
				y->sum=key;
				y->lazy=0;
				y->size=1;
				y->ch[0]=y->ch[1]=NULL;
				y->f=u;
				u->ch[0]=y;
				break;
			}
		}
		else
		{
			if(u->ch[1]!=NULL)u=u->ch[1];
			else
			{
				y=&S[++ncnt];
				y->id=id;
				y->sum=key;
				y->lazy=0;
				y->size=1;
				y->ch[0]=y->ch[1]=NULL;
				y->f=u;
				u->ch[1]=y;
				break;
			}
		}
	}
	splay(y,NULL);
}
char s[5];
node *find(long long x)//id为x的位置 
{
	node *u=root;
	while(1)
	{
		pushdown(u);
		if(u->id==x)return u;
		if(x<u->id)u=u->ch[0];
		else u=u->ch[1];
	}
}
void add(long long l,long long r,long long v)
{
	//把l-1旋到根,把r+1选到根的右边,直接打上lazy标记就好了
	splay(find(l-1),NULL);
	splay(find(r+1),root);
	node *x=root->ch[1]->ch[0];
	x->lazy+=v;
	x->sum+=(x->size)*v; 
	
	//更新父节点的值
	root->ch[1]->sum+=(x->size)*v;
	root->sum+=(x->size)*v; 
}//忘记了更新父节点信息,草草草 
long long query(long long l,long long r)
{
	splay(find(l-1),NULL);
	splay(find(r+1),root);
	return root->ch[1]->ch[0]->sum;
}
void dfs(node *u)//debug
{
	//printf("debug : id: %d sum: %d lazy: %d size: %d\n",u->id,u->sum,u->lazy,u->size);
	printf("%d  ",u->id);printf("lazy:%d  ",u->lazy );printf("size: %d ",u->size );
	printf("sum: %d\n",u->sum);
	if(u->ch[0])dfs(u->ch[0]);
	if(u->ch[1])dfs(u->ch[1]);
}
int main()
{
	while(scanf("%lld%lld",&n,&q)!=EOF)
	{
		root=NULL;		
		ncnt=0;
		insert(0,0);
		insert(0,n+1);
		for(long long i=1;i<=n;i++)
		{
			long long x;
			scanf("%lld",&x);			
			insert(x,i);
		}
		
		for(long long i=1;i<=q;i++)
		{
			scanf("%s",s);
			if(s[0]=='C')
			{
				long long a,b,c;
				scanf("%lld%lld%lld",&a,&b,&c);								
				add(a,b,c);
			}
			else
			{
				long long a,b;
				scanf("%lld%lld",&a,&b);
				printf("%lld\n",query(a,b));
			}
		}
	}
	return 0;
}

尼姆啊。


注:线段树的题是codevs1082的,操作类似,把Q,C改为1,2即可。



膜拜splay的码量。

然后,简述一下splay思路:


每次操作把l-1旋到根,r+1旋到根的下面, root->ch[1]->ch[0]就是[l,r]这一段区间。

然后当成线段树做就可以了(该修改的修改,该下放的下放)。

任何从上到下找的函数每一层都需要pushdown,道理同线段树。


splay真的是功能强大啊,但是代码量也太让人~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值