各种树模板(splay,线段树,可持久化线段树...)

这是裸的排序Splay

AC tyvj1728 普通平衡树


#include 
#include 
#include 

#include 
#include 
#include 
#include 

typedef long long int ll;
typedef double db;

using namespace std;

struct SplayTree
{
	struct node
	{
		int v;
		int tot;
		node*s[2];
		node*f;
		
		void update()
		{
			tot=s[0]->tot + s[1]->tot +1;
		}
	};
	node*pool;
	node*nt;
	node*nil;
	node*newnode(node*f,int v)
	{
		nt->v=v;
		nt->tot=1;
		nt->s[0]=nt->s[1]=nil;
		nt->f=f;
		return nt++;
	}
	
	node*root;
	
	SplayTree(int size)
	{
		pool=new node[size+1];
		nt=pool;
		nil=newnode(NULL,-1);
		nil->tot=0;
		nil->f=nil->s[0]=nil->s[1]=nil;
		root=nil;
	}
	
	//===============================================
	
	void update(node*x)
	{
		x->tot= x->s[0]->tot + x->s[1]->tot +1;
	}
	
	void rot(node*x)
	{
		if(x==nil) return ;
		
		node*y=x->f;
		int k=(x==y->s[0]);
		
		y->s[k^1]=x->s[k];
		if(x->s[k]!=nil) x->s[k]->f=y;
		
		x->f=y->f;
		if(y==y->f->s[0]) y->f->s[0]=x;
		else if(y==y->f->s[1]) y->f->s[1]=x;
		
		y->f=x;
		x->s[k]=y;
		
		y->update();
	}
	
	void splay(node*x) { splay(x,nil); }
	void splay(node*x,node*t)
	{
		if(x==nil) return ;
		while(x->f!=t)
		{
			node*y=x->f;
			if(y->f!=t) 
			if((x==y->s[0])^(y==y->f->s[0]))
				rot(y); else rot(x);
			rot(x);
		}
		x->update();
		
		if(t==nil) root=x;
	}
	
	//=============================================
	
	void Insert(int v)
	{
		if(root==nil)
		{
			root=newnode(nil,v);
			return ;
		}
		
		node*x=root;
		node*y=x;
		while(x!=nil)
		{
			y=x;
			if(vv) x=x->s[0];
			else x=x->s[1];
		}
		
		int k=!(vv);
		y->s[k]=newnode(y,v);
		splay(y->s[k]);
	}
	
	
	node*Find(int v)
	{
		node*x=root;
		node*y=x;
		node*r=nil;
		while(x!=nil)
		{
			y=x;
			if(x->v==v) r=x;
			if(v<=x->v) x=x->s[0];
			else x=x->s[1];
		}
		splay(y);
		return r;
	}
	
	node* FindRank(int k)
	{
		node*x=root;
		node*y=x;
		while(x!=nil)
		{
			y=x;
			if(k==x->s[0]->tot+1) break;
			
			if(ks[0]->tot+1)
				x=x->s[0];
			else
			{
				k-=x->s[0]->tot+1;
				x=x->s[1];
			}
		}
		splay(y);
		return x;
	}
	
	int GetRank(node*x)
	{
		splay(x);
		return x->s[0]->tot+1;
	}
	
	int GetRevRank(node*x)
	{
		splay(x);
		return x->s[1]->tot+1;
	}
	
	node*Delete(node*x)
	{
		int k=GetRank(x);
		node*L=FindRank(k-1);
		node*R=FindRank(k+1);
		
		splay(L);
		splay(R,L);
		
		if(L==nil && R==nil) root=nil;
		else if(R==nil) L->s[1]=nil;
		else R->s[0]=nil;
		
		if(R!=nil) update(R);
		if(L!=nil) update(L);
		
		return x;
	}
	
	node*prefix(int v)
	{
		node*x=root;
		node*y=x;
		node*r=nil;
		while(x!=nil)
		{
			y=x;
			if(x->vv) x=x->s[0];
			else x=x->s[1];
		}
		splay(y);
		return r;
	}
	
	node*suffix(int v)
	{
		node*x=root;
		node*y=x;
		node*r=nil;
		while(x!=nil)
		{
			y=x;
			if(x->v>v) r=x;
			if(vv) x=x->s[0];
			else x=x->s[1];
		}
		splay(y);
		return r;
	}
	
	
	
	
	//===========================================
	void output() { output(root); printf("%s\n",root==nil ? "empty tree!" : ""); }
	void output(node*x)
	{
		if(x==nil)return ;
		output(x->s[0]);
		printf("%d ",x->v);
		output(x->s[1]);
	}
	
	void test() { test(root); printf("%s\n",root==nil ? "empty tree!" : ""); }
	void test(node*x)
	{
		if(x==nil)return ;
		test(x->s[0]);
		printf("%p [ v:%d f:%p L:%p R:%p tot:%d ] \n",x,x->v,x->f,x->s[0],x->s[1],x->tot);
		test(x->s[1]);
	}
	
};


int n;

int main()
{
	scanf("%d",&n);
	SplayTree st(n);
	
	for(int i=0;iv);
			break;
			case 5: //prefix
				scanf("%d",&c);
				printf("%d\n",st.prefix(c)->v);
			break;
			case 6: //suffix
			 	scanf("%d",&c);
			 	printf("%d\n",st.suffix(c)->v);
			break;
			case 7: //test
				st.test();
			break;
			default: break; 
		} 
	}

	return 0;
}




要点1.别忘了有事没事splay一下保证复杂度.....

要点2.各种if的顺序别搞混了!有些if是不能合并的.

要点3.记住splay前的特判.如果要单独使用rotate就给rotate也加特判.

要点4.不要有事没事就更改某些子树的位置!比如在delete的时候,提x作根,然后找到右子树最左边的节点后,合并左右两颗子树,这是会超时的!


代码超长...神犇勿拍....

还有优化的余地.....

另外我发现好多神奔居然除了insert和delete,都不splay....然后我写就狂T........ORZ..

还是不怎么会写二叉查找树的常规....比如FindRank之类....多练就能熟练吧..

这个Splay在TYVJ的T1728上跑了大约400ms. 数据范围是操作数=10W. 鉴于以前写单旋splay能差不多这个时间过,数据应该是随机的....

也就是说...这个Splay的常数大概有20+....好恐怖...我线段树好歹也只有5到6........

也就是说不能用这个模板做树套树之类了TAT

还是找个时间改进吧....


//=================================================================================

可持久化线段树

AC VIJOS 1081 野生动物园

一道非常裸的区间k大

const int INF=(1<<30)-1;

struct node
{
	int t;
	node*l,*r;
	node(){ t=0; l=r=NULL;  }
	
	void update()
	{ t=l->t+r->t; }
}pool[4000000];
int nt;
node*newnode()
{ return &pool[nt++]; }

node*nil;

node*root[100050];

void SegmentTreeInit(int size)
{
	nil=newnode();
	nil->l=nil->r=nil;
	nil->t=0;
	for(int i=0;i<=size;i++)
	root[i]=nil;
}

int cp;
node*Change(node*x,node*y,int l,int r)
{
	if(cpt=1+y->t; return x; }
	int mid=(l+r)>>1;
	x->l=Change(x->l,y->l,l,mid);
	x->r=Change(x->r,y->r,mid+1,r);
	x->update();
	return x;
}
void Change(int i,int j,int pos)
{ cp=pos; root[i]=Change(nil,root[j],0,INF); }

int Query(int ql,int qr,int k)
{
	node*x=root[ql],*y=root[qr];
	int l=0,r=INF;
	while(l!=r)
	{
		int mid=(l+r)>>1;
		if( k<= x->l->t - y->l->t )
			 r=mid,x=x->l,y=y->l;
		else
		{
			k-=x->l->t-y->l->t;
			l=mid+1,x=x->r,y=y->r;
		}
	}
	return l;
}



int n;



int main()
{
	
	int q;
	scanf("%d",&n);
	scanf("%d",&q);
	
	SegmentTreeInit(n);
	
	
	for(int i=0;i

要点1.使用nil节点可以省一点代码

要点2.千万注意空间开销.一般为nlogv级别,数组经常开上百万(懒得写离散化系列)

要点3.注意前缀和的写法. tree[R]-tree[L-1]. 这就要求root[0]=nil.

要点4.智商捉急表示普通查找操作总是写错...splay也一样.....思考...思考......写之前一定要想好....





//====================================================================================

第一次写树套树

AC VIJOS 1665

树状数组 套 动态开点的权值线段树

题目就是裸的带修改区间K大

写了一个多小时...调了大概....一个半小时?

树状数组怎么写都快忘记了........

由于懒得离散化......所以.....开了一个巨大的数组.......

VJ的内存限制不错....先把数组从400W改到800W...还是RE..怒而改到1.3kW...AC了.....

看了看空间消耗.....160M.....

这告诉我们千万不要忽视离散化的力量!  千万不要忽视常数空间!

但是我还是很懒=w=能不写离散化就不写离散化=w=

好吧......

下面是代码.....

附带一大版的文件头以及调试信息2333

#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
 
typedef unsigned int uint;
typedef long long int ll;
typedef unsigned long long int ull;
typedef double db;
 
using namespace std;
 
inline int getint()
{
    int res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
inline ll getll()
{
    ll res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
 
db eps=1e-18;
inline bool feq(db a,db b)
{ return fabs(a-b)>1); }

//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================

const int INF=(1<<30)-1;

int n;
struct node*nil;
struct node
{
	int v; //total
	node*l,*r;
	void update()
	{ v=l->v+r->v; }
}pool[13000000];
node*nt=pool;
node*newnode()
{
	nt->l=nt->r=nil;
	nt->v=0;
	return nt++;
}

int cp,cv;

//Sub segment trees
struct SegmentTree
{
	node*root;
	
	node*Change(node*x,int l=0,int r=INF)
	{
		if(cpv+=cv; return x; }
		int mid=avg(l,r);
		x->l=Change(x->l,l,mid);
		x->r=Change(x->r,mid+1,r);
		x->update();
		return x;
	}
	
	void Set(int p,int v)
	{
		if(root0;i-=bt(i))
	c[adt++]=t[i].root;
	
	ct=adt;
	for(int i=l-1;i>0;i-=bt(i))
	c[ct++]=t[i].root;
	
	//we perform add when il->v;
		for(int i=adt;il->v;
		
		if(k<=clv) //the element we want find is on the left block
		{
			for(int i=0;il;
			r=mid;
		}
		else
		{
			for(int i=0;ir;
			k-=clv;
			l=mid+1;
		}
	}
	
	return l;
}

int q;

int main()
{
	nil=newnode();
	nil->l=nil->r=nil;
	nil->v=0;
	
	n=getint();
	q=getint();
	for(int i=0;i

需要注意的地方:

1.树状数组什么的一级结构别写错了啊啊啊啊啊啊

2.既然是动态开点(即便离散化了也给我动态!绝对不要写静态的树套在另外的树里面!)....

那么,我们只需要记录每棵树的根节点就好了.其它节点在访问的时候会碰到.

3.嗯....(结构相同的)线段树是可加的.......所以不要再去写二分,直接在加起来的那棵树上隐式二分即可.详见代码.可以降低一个log的复杂度.

4.二分的界,以及二分后的操作(k-=...)一定要考虑清楚.

<5.不知道为什么, 用@iwtwiioi在某些地方的AC代码交到VJ,TLE了...就AC了我第一次提交没有RE的那两个范围较小的点...... http://www.cnblogs.com/iwtwiioi/p/3929957.html>

<满满的成就感><我估计马上会被吐槽>







//====================================================================================

不带其余任何标记的,带换根的LCT.

AC BZOJ2049 一道非常裸的LCT

#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
 
typedef unsigned int uint;
typedef long long int ll;
typedef unsigned long long int ull;
typedef double db;
 
using namespace std;
 
inline int getint()
{
    int res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
inline ll getll()
{
    ll res=0;
    char c=getchar();
    bool mi=false;
    while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
    while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
    return mi ? -res : res;
}
 
db eps=1e-18;
inline bool feq(db a,db b)
{ return fabs(a-b)
inline Type avg(const Type a,const Type b)
{ return a+((b-a)/2); }

//==============================================================================
//==============================================================================
//==============================================================================
//==============================================================================





struct node* nil;

struct node
{
	bool rev;
	node*s[2],*f;

	bool root() 
	{ return this!=f->s[0] && this!=f->s[1]; }
	
	void pushtag()
	{
		if(rev)
		{
			s[0]->rev^=1;
			s[1]->rev^=1;
			swap(s[0],s[1]);
			rev=false;
		}
	}
	
};


struct LinkCutTree
{
	node*nt;
	LinkCutTree(int size)
	{
		nt=new node[size];
		for(int i=0;iroot()) cleartag(x->f); x->pushtag(); }
	
	node*operator[](int k)
	{ return nt+k; }
	
	void rot(node*x)
	{
		if(x->root()) return ;
		node*y=x->f;
		bool k=(x==y->s[0]);
		
		y->s[!k]=x->s[k];
		if(x->s[k]!=nil) x->s[k]->f=y;
		
		x->f=y->f;
		if(y==y->f->s[0]) y->f->s[0]=x;
		else if(y==y->f->s[1]) y->f->s[1]=x;
		
		y->f=x;
		x->s[k]=y;
	}
	
	void splay(node*x)
	{
		cleartag(x);
		while(!x->root())
		{
			node*y=x->f;
			if(!y->root())
			{
				if((x==y->s[0])^(y==y->f->s[0]))
				rot(y); else rot(x);
			}
			rot(x);
		}
	}
	
	node*access(node*x)
	{
		node*y=nil;
		node*t=x;
		while(t!=nil)
		{
			splay(t);
			t->s[0]=y;
			y=t;
			t=t->f;
		}
		splay(x);
		return x;
	}
	
	node*FindRoot(node*x)
	{
		access(x);
		while(x->s[1]!=nil) x=x->s[1];
		return x;
	}
	
	node*SetRoot(node*x)
	{
		access(x)->rev^=1;
		return x;
	}
	
	void Link(node*x,node*y)
	{
		SetRoot(x)->f=y;
	}
	
	void Cut(node*x,node*y)
	{
		SetRoot(x);
		access(y);
		y->s[1]->f=nil;
		y->s[1]=nil;
	}
	
	void output(int i)
	{ cout<s[0]=nil->s[1]=nil->f=nil;
	
	n=getint();
	m=getint();
	
	LinkCutTree t(n);
	
	for(int i=0;i

很奇怪,Link,Cut,FindRoot和SetRoot这些函数换一种写法就各种TLE/RTE,还有cleartag()也是一样,不知道为什么.....TAT

你可能感兴趣的:(模板,OI)