POJ 3580

splay超全模板题...

总共有6种操作,分别为MIN,INSERT,ADD,DELETE,REVERSE,REVOLVE六种,下面会分别介绍格式与方法

首先输入N以及N个数,接下来输入M,即操作个数,接下来M行包含操作,具体格式如下

1.MIN a b 求a,b区间的最小值

2.ADD a b c 对a,b区间的每个数添加c

3.DELETE a 将第a个数删除

4.INSERT a b 在第a个数后面添加一个数b

5.REVERSE a,b 将a,b区间每个数翻转 如 REVERSE 2 4 (1,2,3,4,5)-->(1,4,3,2,5)

6.REVOLVE a,b,c 将a,b区间作为一个环 然后旋转c次(这是我的理解... ...)

如 REVOVLE 2 4 1 (1,2,3,4,5)-->(1,4,2,3,5)

REVOVLE 2 4 2 (1,2,3,4,5)-->(1,3,4,2,5)

然后这里面的c有可能是负数并且很大... ... 所以要取模

讲一下算法思想:

构建一棵splay树 储存父亲 左右儿子 翻转标记 增加标记 最小值 本身的值 以此节点为根的子树的节点数 (size)

首先将1~n节点添加进去 同时要在最前面和最后面添加一个节点(具体理由后面讲)

基本函数:update(更新这个节点的MIN值与size),splay(将一个点旋转到目标位置),rotateto(找到第k个节点并旋转到目标位置),pushdown(将增加标记(add)与翻转标记(rev)推到下一层节点),newnode(构建一个新节点,节点序号为cnt)

讲讲每个操作的具体操作方法

1.MIN a,b 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,注意:因为前面添加了一个节点,所以原来第a个节点为现在的第a+1个节点 所以现在根节点右节点的左子树就代表以前的 a,b区间,只要输出它的MIN就可以了

2.ADD a,b,c 同理 将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树增加标记与数值,最小值添加c

3.DELETE a 将第a个节点旋到根节点,第a+2个节点旋到根节点的右节点,那么根节点右节点的左子树有且仅有a+1这个节点(即原来的第a个节点),那么断绝他们的父子关系就可以了

4.INSERT a b 将第a个节点旋到根节点,第a+1个节点旋到根节点的右节点,那么根节点右节点的左子树绝对没有节点,同样添加一个值为b的新节点,然后建立关系

5.REVERSE a,b 与ADD相似 只要改变一下将第a个节点旋到根节点,将第b+2个节点旋到根节点的右子树,给根节点右节点的左子树更改REV标记

6.REVOLVE a,b,c这个操作有一点复杂... ...实际上可以看出 这就是将这个区间里面分成两个部分然后交换位置,那我们先通过旋转找到这一段区间,将它从父亲节点那里并记录位置,然后又找到另一端区间的位置,将之前存下来区间放在这个区间的前面,就可以实现,这一段需要一定的理解,可以手动模拟一下发现规律,具体见代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=400010;
using namespace std;
int rt,m,n,a[maxn],cnt;
int val[maxn],Min[maxn],add[maxn],rev[maxn];
int ls[maxn],rs[maxn],fa[maxn],sz[maxn];
void newnode(int v)
{
	sz[++cnt]=1;
	val[cnt]=Min[cnt]=v;
	fa[cnt]=ls[cnt]=rs[cnt]=add[cnt]=rev[cnt]=0;
}
void update(int x)
{
	sz[x]=sz[ls[x]]+sz[rs[x]]+1;
	Min[x]=val[x];
	if(ls[x]&&Min[ls[x]]<Min[x])Min[x]=Min[ls[x]];
	if(rs[x]&&Min[rs[x]]<Min[x])Min[x]=Min[rs[x]];
}
void pushdown(int x)
{
	if(add[x]!=0)
	{
		if(ls[x])
		{
			add[ls[x]]+=add[x];
			Min[ls[x]]+=add[x];
			val[ls[x]]+=add[x];
		}
		if(rs[x])
		{
			add[rs[x]]+=add[x];
			Min[rs[x]]+=add[x];
			val[rs[x]]+=add[x];
		}
		add[x]=0;
	}
	if(rev[x])
	{
		if(ls[x])rev[ls[x]]^=1;
		if(rs[x])rev[rs[x]]^=1;
		swap(ls[x],rs[x]);
		rev[x]=0;
	}
}
void build(int x,int f)
{
	if(x==n+2)return;
	newnode(a[x]);
	if(f)
	{
		fa[cnt]=f;
		rs[f]=cnt;
	}
	else rt=cnt;
	int now=cnt;
	build(x+1,cnt);
	update(now);
}
void init()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	build(0,0);
}
void rrotate(int x)
{
	int y=ls[x],k=fa[x];
	ls[x]=rs[y];
	if(ls[x])fa[ls[x]]=x;
	fa[x]=y;fa[y]=k;rs[y]=x;
	if(k==0)rt=y;
	else 
	if(ls[k]==x)ls[k]=y;
	else rs[k]=y;
	update(x);
}
void lrotate(int x)
{
	int y=rs[x],k=fa[x];
	rs[x]=ls[y];
	if(rs[x])fa[rs[x]]=x;
	ls[y]=x;fa[x]=y;fa[y]=k;
	if(k==0)rt=y;
	else 
	if(ls[k]==x)ls[k]=y;
	else rs[k]=y;
	update(x);
}
void splay(int x,int f)
{
	int y,z;
	while(1)
	{
		if((y=fa[x])==f)break;
		if((z=fa[y])==f)
		{
			if(ls[y]==x)rrotate(y);
			else lrotate(y);
		}
		else
		{
			if(ls[z]==y)
			{
				if(ls[y]==x)rrotate(z),rrotate(y);
				else lrotate(y),rrotate(z);
			}
			else
			{
				if(rs[y]==x)lrotate(z),lrotate(y);
				else rrotate(y),lrotate(z);
			}
		}
	}
	return;
}
void rotateto(int k,int f)
{
	int i=rt;
	while(1)
	{
		pushdown(i);
		int lsum=sz[ls[i]]+1;
		if(k==lsum)break;
		if(k<lsum)i=ls[i];
		else k-=lsum,i=rs[i];
	}
	splay(i,f);
}
void ADD(int x,int y,int z)
{
	rotateto(x,0);
	rotateto(y+2,rt);
	int k=ls[rs[rt]];
	add[k]+=z,val[k]+=z,Min[k]+=z;
}
void insert(int x,int y)
{
	rotateto(x+1,0);rotateto(x+2,rt);
	newnode(y);
	ls[rs[rt]]=cnt;
	fa[cnt]=rs[rt];
}
void del(int x)
{
	rotateto(x,0);rotateto(x+2,rt);
	ls[rs[rt]]=0;
}
void MIN(int x,int y)
{
	rotateto(x,0);rotateto(y+2,rt);
	printf("%d\n",Min[ls[rs[rt]]]);
}
void reverse(int x,int y)
{
	if(x==y)return;
	rotateto(x,0);rotateto(y+2,rt);
	rev[ls[rs[rt]]]^=1;
}
void revolve(int x,int y,int z)
{
	int p=(y-x+1),k,t;
	k=(z%p+p)%p;
	if(k)
	{
		rotateto(y-k+1,0);rotateto(y+2,rt);
		t=ls[rs[rt]];
		ls[rs[rt]]=0;
		rotateto(x,0);rotateto(x+1,rt);
		ls[rs[rt]]=t;
		fa[t]=rs[rt];
	}
}
void solve()
{
	int x,y,z;
	char s[10];
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s);
		if(s[0]=='A')
		{
			scanf("%d%d%d",&x,&y,&z);
			ADD(x,y,z);
		}
		else
		if(s[0]=='I')
		{
			scanf("%d%d",&x,&y);
			insert(x,y);
		}
		else
		if(s[0]=='D')
		{
			scanf("%d",&x);
			del(x);
		}
		else
		if(s[0]=='M')
		{
			scanf("%d%d",&x,&y);
			MIN(x,y);
		}
		else
		if(s[0]=='R')
		{
			if(s[3]=='O')
			{
				scanf("%d%d%d",&x,&y,&z);
				revolve(x,y,z);
			}
			else
			{
				scanf("%d%d",&x,&y);
				reverse(x,y);
			}
		}
	}
	return;
}
int main()
{
	init();
	solve();
	return 0;
}
好像差不多了... ...代码AC了,但时间有点长... ...毕竟还是第一次做个题目,借鉴了一下别人的代码... ...

更多推荐

POJ 3580 splay模板题