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

来源:互联网 发布:人工智能养猪 编辑:程序博客网 时间:2024/06/05 03:31

这是裸的排序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: //Insertscanf("%d",&c);st.Insert(c);break;case 2: //Deletescanf("%d",&c);st.Delete(st.Find(c));break;case 3: //Rankscanf("%d",&c);printf("%d\n",st.GetRank(st.Find(c)));break;case 4: //FindRankscanf("%d",&c);printf("%d\n",st.FindRank(c)->v);break;case 5: //prefixscanf("%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: //testst.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; //totalnode*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 treesstruct 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<ctl=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

0 0