这是裸的排序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
要点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
需要注意的地方:
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