这是裸的排序Splay
AC tyvj1728 普通平衡树
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
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(v<x->v) x=x->s[0];
else x=x->s[1];
}
int k=!(v<y->v);
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(k<x->s[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->v<v) r=x;
if(v<=x->v) 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(v<x->v) 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;i<n;i++)
{
int c;
scanf("%d",&c);
switch(c)
{
case 1: //Insert
scanf("%d",&c);
st.Insert(c);
break;
case 2: //Delete
scanf("%d",&c);
st.Delete(st.Find(c));
break;
case 3: //Rank
scanf("%d",&c);
printf("%d\n",st.GetRank(st.Find(c)));
break;
case 4: //FindRank
scanf("%d",&c);
printf("%d\n",st.FindRank(c)->v);
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(cp<l || r<cp) return y;
x=newnode();
if(l==r) { x->t=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<n;i++)
{
int c;
scanf("%d",&c);
cp=c;
root[i+1]=Change(root[i+1],root[i],0,INF);
}
for(int i=0;i<q;i++)
{
int a,b,k;
scanf("%d%d%d",&a,&b,&k);
printf("%d\n",Query(b,a-1,k));
}
return 0;
}
要点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 <cstdio>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>
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)<eps; }
inline int avg(const int a,const int b)
{ return a+((b-a)>>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(cp<l || r<cp) return x;
if(x==nil) x=newnode();
if(l==r) { x->v+=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(root<pool) root=nil;
cp=p;
cv=v;
root=Change(root);
}
};
//original segment tree
//performed as tree array
#define bt(x) (x&-x)
int a[1000000]; //this array must be stay here....
SegmentTree t[1000000];
void Change(int p,int s,int v) //location of point, value of point, delete or add in.
{ for(int i=p;i<=n;i+=bt(i)) t[i].Set(s,v); }
node*c[1000];
int adt,ct;
int Query(int l,int r,int k) //find the element which is rank k.
{
adt=0;
for(int i=r;i>0;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 i<adt, and than dec when adt<=i<ct
l=0,r=INF;
while(l!=r)
{
//for(int i=0;i<ct;i++)
//cout<<c[i]<<' '; cout<<endl; cout<<l<<' '<<r<<endl; cout<<endl;
int mid=avg(l,r);
int clv=0; //current node's left node count.
for(int i=0;i<adt;i++)
clv+=c[i]->l->v;
for(int i=adt;i<ct;i++)
clv-=c[i]->l->v;
if(k<=clv) //the element we want find is on the left block
{
for(int i=0;i<ct;i++)
c[i]=c[i]->l;
r=mid;
}
else
{
for(int i=0;i<ct;i++)
c[i]=c[i]->r;
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<n;i++)
Change(i+1,a[i+1]=getint(),1);
for(int i=0;i<q;i++)
{
char c[5];
scanf("%s",c);
if(c[0]=='C')
{
int i=getint();
int v=getint();
Change(i,a[i],-1);
Change(i,a[i]=v,1);
}
else
{
int i=getint();
int j=getint();
int k=getint();
printf("%d\n",Query(i,j,k));
}
}
return 0;
}
需要注意的地方:
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 <cstdio>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>
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)<eps; }
template<typename Type>
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;i<size;i++)
{
nt[i].s[0]=nt[i].s[1]=nt[i].f=nil;
nt[i].rev=false;
}
}
void cleartag(node*x)
{ if(!x->root()) 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<<i<<' '<<&nt[i]<<' '<<nt[i].s[0]<<' '<<nt[i].s[1]<<' '<<nt[i].f<<endl; }
};
int n,m;
int main()
{
nil=new node;
nil->s[0]=nil->s[1]=nil->f=nil;
n=getint();
m=getint();
LinkCutTree t(n);
for(int i=0;i<m;i++)
{
char c[20];
scanf("%s",c);
if(c[0]=='C') //Link
{
t.Link(t[getint()-1],t[getint()-1]);
}
else if(c[0]=='D') //Cut
{
t.Cut(t[getint()-1],t[getint()-1]);
}
else if(c[0]=='Q') //Query
{
if(t.FindRoot(t[getint()-1])==t.FindRoot(t[getint()-1])) printf("Yes\n");
else printf("No\n");
}
}
return 0;
}
很奇怪,Link,Cut,FindRoot和SetRoot这些函数换一种写法就各种TLE/RTE,还有cleartag()也是一样,不知道为什么.....TAT