FHQ Treap,又称无旋treap,一种不基于旋转机制的平衡树,可支持所有有旋treap、splay等能支持的操作(只有在LCT中会比splay复杂度多一个log)。最重要的是,它是OI中唯一一种支持可持久化的平衡树。

以下只提供题表与代码,不提供教程。

1.[BZOJ3224]普通平衡树

FHQ Treap的应用一:基础平衡树操作模板题。

由于merge、split和树高是$O(\log n)$的,所以所有基础操作都是$O(\log n)$的。

 #include<cstdio>
#include<algorithm>
#define P pair<int,int>
#define rep(i,l,r) for (int i=l; i<=r; i++)
using namespace std; const int N=,inf=;
int T,nd,rt,op,ans,x,ls[N],rs[N],h[N],sz[N],v[N]; void upd(int x){ sz[x]=sz[ls[x]]+sz[rs[x]]+; }
int get(int x){ v[++nd]=x; sz[nd]=; h[nd]=rand(); return nd; } int merge(int x,int y){
if (!x || !y) return x+y;
if (h[x]<h[y]) { rs[x]=merge(rs[x],y); upd(x); return x; }
else { ls[y]=merge(x,ls[y]); upd(y); return y; }
} P split(int x,int k){
if (!x) return P(,);
P tmp;
if (k<=sz[ls[x]]) tmp=split(ls[x],k),ls[x]=tmp.second,upd(x),tmp=P(tmp.first,x);
else tmp=split(rs[x],k-sz[ls[x]]-),rs[x]=tmp.first,upd(x),tmp=P(x,tmp.second);
return tmp;
} int rank(int x,int k){
if (!x) return ;
if (v[x]>=k) return rank(ls[x],k); else return rank(rs[x],k)+sz[ls[x]]+;
} int find(int x,int k){
if (!x) return ;
if (k==sz[ls[x]]+) return x;
if (k<=sz[ls[x]]) return find(ls[x],k); else return find(rs[x],k-sz[ls[x]]-);
} void ins(int k){
int v=rank(rt,k); P x=split(rt,v);
rt=merge(merge(x.first,get(k)),x.second);
} void del(int k){
int v=rank(rt,k);
P x=split(rt,v),y=split(x.second,);
rt=merge(x.first,y.second);
} int main(){
for (scanf("%d",&T); T--; ){
scanf("%d%d",&op,&x);
if (op==) ins(x);
if (op==) del(x);
if (op==) printf("%d\n",rank(rt,x)+);
if (op==) printf("%d\n",v[find(rt,x)]);
if (op==) printf("%d\n",v[find(rt,rank(rt,x))]);
if (op==) printf("%d\n",v[find(rt,rank(rt,x+)+)]);
}
return ;
}

BZOJ3224

2.[CF702F]T-shirts

FHQ Treap的应用二:树的分裂与合并、树上延迟标记。

n种物品(ci,qi)k个人(bi),每个人将所有物品按qi从大到小,相同q按ci从小到大依次考虑,若剩余钱数bi>=ci则买一件否则跳过,问每个人买了几件物品。

由于每个人考虑物品的顺序是一样的,考虑从物品分配给人的角度处理。将物品按题目要求排序后依次处理,每次将所有钱数足够的人钱数集体减c。

对人按剩余钱数建Treap,当考虑物品i时,将树按钱数是否足够分裂成两边,并在足够的一边打上集体-c的标记。此时树中节点已无序,于是每次暴力将右边的最小值插入到左边直到右边最小值大于左边最大值为止,最后再将两边合并起来。

考虑复杂度,当一个人x被从右边转到左边时,有x-c<c,又由于c<=x,故x-c<x/2,即每次至少减半,于是总复杂度均摊$O(n\log^2 n)$

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int m,n,rt,nd,res,id[N],b[N],ans[N];
struct Q{ int q,c; }s[N];
struct Tr{ int ls,rs,sz,k,tag2,hr,id; ll v,tag1; }v[N];
struct P{ int x,y; };
bool operator <(const Q &a,const Q &b){ return a.q!=b.q ? a.q>b.q : a.c<b.c; }
bool cmp(int x,int y){ return b[x]<b[y]; } void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+; }
void put(int x,ll w,int k){ v[x].v-=w; v[x].k+=k; v[x].tag1+=w; v[x].tag2+=k; } void push(int x){
if (v[x].tag1 || v[x].tag2){
put(v[x].ls,v[x].tag1,v[x].tag2);
put(v[x].rs,v[x].tag1,v[x].tag2);
v[x].tag1=v[x].tag2=;
}
} int merge(int x,int y){
if (!x || !y) return x|y;
if (v[x].hr<v[y].hr){ push(x); v[x].rs=merge(v[x].rs,y); upd(x); return x; }
else{ push(y); v[y].ls=merge(x,v[y].ls); upd(y); return y; }
} P split(int x,int k){
if (!x) return (P){,};
push(x);
if (k<v[x].v){ P t=split(v[x].ls,k); v[x].ls=t.y; upd(x); return (P){t.x,x}; }
else{ P t=split(v[x].rs,k); v[x].rs=t.x; upd(x); return (P){x,t.y}; }
} int ins(int x,int k){ P t=split(x,v[k].v); return merge(merge(t.x,k),t.y); } int del(int x){
if (!v[x].ls){ int t=v[x].rs; v[x].rs=; return t; }
v[x].ls=del(v[x].ls); upd(x); return x;
} int Mn(int x){ push(x); return v[x].ls ? Mn(v[x].ls) : x; }
int Mx(int x){ push(x); return v[x].rs ? Mx(v[x].rs) : x; } void dfs(int x){
push(x);
if (v[x].ls) dfs(v[x].ls);
ans[v[x].id]=v[x].k;
if (v[x].rs) dfs(v[x].rs);
} int main(){
scanf("%d",&m);
rep(i,,m) scanf("%d%d",&s[i].c,&s[i].q);
sort(s+,s+m+); scanf("%d",&n);
rep(i,,n) scanf("%d",&b[i]),id[i]=i;
sort(id+,id+n+,cmp);
rep(i,,n) v[++nd]=(Tr){,,,,,rand(),id[i],b[id[i]],},rt=merge(rt,nd);
rep(i,,m){
P t=split(rt,s[i].c-); put(t.y,s[i].c,);
while (){
int x=Mn(t.y); if (!x) break;
int y=Mx(t.x); if (!y) break;
if (v[x].v>=v[y].v) break;
t.y=del(t.y); t.x=ins(t.x,x);
}
rt=merge(t.x,t.y);
}
dfs(rt);
rep(i,,n) printf("%d ",ans[i]);
return ;
}

CF702F

3.[Luogu3835][模板]可持久化平衡树 && [Luogu5055][模板]可持久化文艺平衡树

FHQ Treap的应用三:可持久化与可持久化延迟标记。

与主席树相对于普通线段树的关系类似,可持久化时,在所有需要修改时新建节点即可。(注意下传标记的时候的两个儿子因为被更改也需要新建)

注意push()和upd(),以及空间复杂度$O(n\log n)$

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=,inf=;
int n,w,op,x,nd,ans,rt[N];
struct Tr{ int ls,rs,hr,v,sz; }v[N*];
struct P{ int x,y; }; void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+; }
int get(int x){ v[++nd].sz=; v[nd].v=x; v[nd].hr=rand(); return nd; } int merge(int x,int y){
if (!x || !y) return x|y;
int p=++nd;
if (v[x].hr<v[y].hr){ v[p]=v[x]; v[p].rs=merge(v[p].rs,y); upd(p); return p; }
else { v[p]=v[y]; v[p].ls=merge(x,v[p].ls); upd(p); return p; }
} P split(int x,int k){
if (!x) return (P){,};
P t; int p=++nd; v[p]=v[x];
if (k<v[p].v){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
else { t=split(v[p].rs,k); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
} int ins(int x,int k){ P t=split(x,k); return merge(merge(t.x,get(k)),t.y); } int del(int x,int k){
P t1=split(x,k),t2=split(t1.x,k-);
t2.y=merge(v[t2.y].ls,v[t2.y].rs);
return merge(merge(t2.x,t2.y),t1.y);
} int rnk(int x,int k){
if (!x) return ;
if (k<=v[x].v) return rnk(v[x].ls,k); else return v[v[x].ls].sz++rnk(v[x].rs,k);
} int que(int x,int k){
if (k==v[v[x].ls].sz+) return v[x].v;
if (k<=v[v[x].ls].sz) return que(v[x].ls,k);
else return que(v[x].rs,k-v[v[x].ls].sz-);
} void pre(int x,int k){
if (!x) return;
if (k>v[x].v) ans=max(ans,v[x].v),pre(v[x].rs,k); else pre(v[x].ls,k);
} void nxt(int x,int k){
if (!x) return;
if (k<v[x].v) ans=min(ans,v[x].v),nxt(v[x].ls,k); else nxt(v[x].rs,k);
} int main(){
freopen("P3835.in","r",stdin);
freopen("P3835.out","w",stdout);
scanf("%d",&n);
rep(i,,n){
scanf("%d%d%d",&w,&op,&x); rt[i]=rt[w];
if (op==) rt[i]=ins(rt[w],x);
if (op==) rt[i]=del(rt[w],x);
if (op==) printf("%d\n",rnk(rt[w],x)+);
if (op==) printf("%d\n",que(rt[w],x));
if (op==) ans=-inf,pre(rt[w],x),printf("%d\n",ans);
if (op==) ans=inf,nxt(rt[w],x),printf("%d\n",ans);
}
return ;
}

Luogu3835

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
ll ans;
int n,w,op,l,r,k,x,nd,rt[N];
struct Tr{ int ls,rs,v,sz,tag,hr; ll sm; }v[N*];
struct P{ int x,y; }; void upd(int x){
v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+;
v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm+v[x].v;
} int get(int x){ nd++; v[nd].v=v[nd].sm=x; v[nd].sz=; v[nd].hr=rand(); return nd; } void put(int &x){ if (x) v[++nd]=v[x],x=nd,swap(v[x].ls,v[x].rs),v[x].tag^=; }
void push(int x){ if (v[x].tag) put(v[x].ls),put(v[x].rs),v[x].tag=; } int merge(int x,int y){
if (!x || !y) return x|y;
int p=++nd;
if (v[x].hr<v[y].hr){ v[p]=v[x]; push(p); v[p].rs=merge(v[p].rs,y); upd(p); return p; }
else{ v[p]=v[y]; push(p); v[p].ls=merge(x,v[p].ls); upd(p); return p; }
} P split(int x,int k){
if (!x) return (P){,};
P t; int p=++nd; v[p]=v[x]; push(p);
if (k<=v[v[x].ls].sz){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
else{ t=split(v[p].rs,k-v[v[x].ls].sz-); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
} int ins(int x,int pos,int k){ P t=split(x,pos); return merge(merge(t.x,get(k)),t.y); } int del(int x,int k){ P t1=split(x,k),t2=split(t1.x,k-); return merge(t2.x,t1.y); } int rev(int x,int l,int r){
P t1=split(x,l-),t2=split(t1.y,r-l+);
put(t2.x); return merge(t1.x,merge(t2.x,t2.y));
} ll que(int x,int l,int r){ P t1=split(x,l-),t2=split(t1.y,r-l+); return v[t2.x].sm; } int main(){
freopen("P5055.in","r",stdin);
freopen("P5055.out","w",stdout);
scanf("%d",&n);
rep(i,,n){
scanf("%d%d",&w,&op);
if (op==) scanf("%d%d",&x,&k),x^=ans,k^=ans,rt[i]=ins(rt[w],x,k);
if (op==) scanf("%d",&x),x^=ans,rt[i]=del(rt[w],x);
if (op==) scanf("%d%d",&l,&r),l^=ans,r^=ans,rt[i]=rev(rt[w],l,r);
if (op==) scanf("%d%d",&l,&r),l^=ans,r^=ans,rt[i]=rt[w],printf("%lld\n",ans=que(rt[w],l,r));
}
return ;
}

Luogu5055

4.[CF1056G]Take Matro

FHQ Treap的应用四:区间复制与朝鲜树式重构.

n个地铁站围成环,若当前位于前m个地铁站则顺时针坐t站否则逆时针坐t站。每坐一次t--。

给定n,m,初始位于的地铁站与t的初值,问最终位于的地铁站。(n,m<=1e5,t<=1e12)

由于每次只与t%n有关,所以首先暴力走成t是n的倍数,然后预处理出从每个站开始走n次(即t=n,t=n-1,...,t=1各走一次)后到的地点,最后倍增到t/n即为答案。

问题就在于如何求出每个点走n次到的位置,对n个地铁站建Treap然后发现每次相当于一个区间平移操作,使用FHQ Treap的区间复制操作即可。

一下内容均为口胡,来自一位神仙提出的方法:https://www.cnblogs.com/Gloid/p/10388719.html

具体来说就是:通过可持久化split复制得到一个区间,重新按顺序拼成一个新的序列就是新的答案了。实现过程主要有几个问题:

1.根据FHQ Treap的时间复杂度证明发现,当将一个区间复制到另一颗树中某位置进行merge的时候,由于两棵树之间的随机权值是互不相关的,所以概率分析实际上是错误的,所以复杂度是没有保证的。

为了解决这一问题,考虑每次合并需要的正确概率,x中最小元素比y中最小元素小的概率(即将y合并到x树上的概率)为$\frac{size[x]}{size[x]+size[y]}$,于是我们合并的时候直接采用随机函数,以这个概率合并即可。(即rand()%(size[x]+size[y])<size[x]))

2.即便考虑了情况1,FHQ Treap也无法保证树高,于是考虑定期重构。

一种方法是每进行一定次数操作就暴力将整棵树拍扁重构,实测效果最好的位于每5000次操作重构一次附近。

一种方法是考虑替罪羊树,当子树重量失衡时重构子树,但发现在可持久化树中,仅重构一棵子树是比较复杂的事情(也可能是我水平低没有想到什么好方法),于是考虑使用朝鲜树,即对每个点维护在树中的高度,当整棵树的高度超过预设上限时重构整棵树。实测高度上限在100左右最优。

当然,由于有拍扁重构的过程,需要使用(1)中的随机合并方法,因为在建树时如果暴力merge复杂度是$O(n\log n)$的,普通建树是线性的,而普通建树时是不给每个节点随机权值并调整的。

 #include<cstdio>
#include<algorithm>
#include<iostream>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
ll t;
int n,m,s,nd,rt,tot,to[N][];
struct Tr{ int ls,rs,v,sz,d; }v[N<<];
struct P{ int x,y; }; int jump(ll x,ll y){
if (x<=m) x+=y; else x-=y;
return (x%n+n-)%n+;
} void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+; v[x].d=max(v[v[x].ls].d,v[v[x].rs].d)+; }
int get(int k){ v[++nd]=(Tr){,,k,,}; return nd; }
void dfs(int x){ if (v[x].ls) dfs(v[x].ls); to[++tot][]=v[x].v; if (v[x].rs) dfs(v[x].rs); }
void Print(int x){ if (v[x].ls) Print(v[x].ls); printf("%d ",v[x].v); if (v[x].rs) Print(v[x].rs); } int merge(int x,int y){
if (!x || !y){ v[++nd]=v[x|y]; return nd; }
int p=++nd;
if (1ll*rand()*(v[x].sz+v[y].sz)<1ll*v[x].sz*RAND_MAX)
{ v[p]=v[x]; v[p].rs=merge(v[p].rs,y); upd(p); return p; }
else{ v[p]=v[y]; v[p].ls=merge(x,v[p].ls); upd(p); return p; }
} P split(int x,int k){
if (!x) return (P){,};
P t; int p=++nd; v[p]=v[x];
if (k<=v[v[x].ls].sz){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
else{ t=split(v[p].rs,k-v[v[x].ls].sz-); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
} int build(int L,int R){
if (L>R) return ;
int mid=(L+R)>>,x=get(to[mid][]);
v[x].ls=build(L,mid-); v[x].rs=build(mid+,R);
upd(x); return x;
} int main(){
freopen("1056G.in","r",stdin);
freopen("1056G.out","w",stdout);
scanf("%d%d%d",&n,&m,&s); cin>>t;
for (; t%n; t--) s=jump(s,t); t/=n;
rep(i,,n) to[i][]=i; rt=build(,n);
rep(i,,n-){
int lst=rt;
if (m+i<=n){ P a=split(lst,m+i),b=split(a.x,i); rt=b.y; }
else{ P a=split(lst,i),b=split(a.x,m+i-n); rt=merge(a.y,b.x); }
if (m+-i>){ P a=split(lst,n-i),b=split(a.x,m-i); rt=merge(rt,b.y); }
else{ P a=split(lst,m+n-i),b=split(a.x,n-i); rt=merge(rt,merge(a.y,b.x)); }
if (v[rt].d>) tot=,dfs(rt),nd=,rt=build(,n);
}
tot=; dfs(rt);
rep(j,,) rep(i,,n) to[i][j]=to[to[i][j-]][j-];
for (int j=; ~j; j--) if (t&(1ll<<j)) s=to[s][j];
printf("%d\n",s);
return ;
}

CF1056G

5.[HDU6087]Rikka with Sequence

FHQ Treap的应用五:快速幂区间循环复制。

当然这只是一个小应用,题意要求支持三种操作:

1.求A[l,r]的和。 在Treap中维护子树和即可。

3.将A[l,r]还原为最初始时的值。这个使用(4)中的区间复制即可。

2.for (int i=l; i<=r; i++) A[i]=A[i-k]。

发现这实际上是将[l-k,l-1]循环复制到[l,r]上。为了降低复杂度,使用类似快速幂的思想。每次自我复制一份,最后再用split()将剩余部分除去。

这题最毒瘤的地方在于它给$O(n\log^2 n)$空间复杂度的程序只开64M空间(可能是有比我复杂度优的做法),于是要注意定期重构。我的代码由于空间问题无法使用朝鲜树式重构,于是选择每1000次操作重构一次。当然还有一种方法是用替罪羊的判定方法,当某子树失衡时暴力重构整棵树,但我没有实现这种方法。

由于2操作,时空复杂度都是$O(n\log^2 n)$的。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
int n,m,tmp,nd,k,op,lst,tot,l,r,rt,a[N];
struct Tr{ int ls,rs,v,sz; ll sm; }v[N*];
struct P{ int x,y; }; int get(int k){ v[++nd]=(Tr){,,k,,k}; return nd; } void upd(int x){
v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+;
v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm+v[x].v;
} int merge(int x,int y){
if (!x || !y){ v[++nd]=v[x|y]; return nd; }
int p=++nd;
if (rand()%(v[x].sz+v[y].sz)<=v[x].sz)
v[p]=v[x],v[p].rs=merge(v[p].rs,y); else v[p]=v[y],v[p].ls=merge(x,v[p].ls);
upd(p); return p;
} P split(int x,int k){
if (!x) return (P){,};
int p=++nd; v[p]=v[x];
if (k<=v[v[x].ls].sz){ P t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
else{ P t=split(v[p].rs,k-v[v[x].ls].sz-); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
} int copy(int x,int l,int r){ P t=split(x,r); return split(t.x,l-).y; } int ksm(int a,int b){
int res=;
for (; b; a=merge(a,a),b>>=)
res=merge(res,a);
return res;
} void dfs(int x){
if (v[x].ls) dfs(v[x].ls);
a[++tot]=v[x].v;
if (v[x].rs) dfs(v[x].rs);
} int build(int L,int R){
if (L>R) return ;
int mid=(L+R)>>,x=get(a[mid]);
v[x].ls=build(L,mid-); v[x].rs=build(mid+,R);
upd(x); return x;
} int main(){
freopen("hdu6087.in","r",stdin);
freopen("hdu6087.out","w",stdout);
scanf("%d%d",&n,&m); int lim=;
rep(i,,n) scanf("%d",&a[i]);
rt=build(,n); tmp=nd; lst=rt;
while (m--){
scanf("%d%d%d",&op,&l,&r);
if (op==) printf("%lld\n",v[copy(rt,l,r)].sm);
if (op==){
scanf("%d",&k);
int x=split(ksm(copy(rt,l-k,l-),(r-l+)/k+),r-l+).x;
P t1=split(rt,l-),t2=split(t1.y,r-l+);
rt=merge(t1.x,merge(x,t2.y));
}
if (op==){
P t1=split(rt,l-),t2=split(t1.y,r-l+);
rt=merge(t1.x,merge(copy(lst,l,r),t2.y));
}
if (!(m%lim)) tot=,dfs(rt),nd=tmp,rt=build(,tot);
}
return ;
}

HDU6087

FHQ Treap及其可持久化与朝鲜树式重构的更多相关文章

  1. 【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 15112  Solved: 4996[Submit][Statu ...

  2. Luogu P3835 【模板】可持久化平衡树(fhq Treap)

    P3835 [模板]可持久化平衡树 题意 题目背景 本题为题目普通平衡树的可持久化加强版. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本 ...

  3. 可持久化treap(FHQ treap)

    FHQ treap 的整理 treap = tree + heap,即同时满足二叉搜索树和堆的性质. 为了使树尽可能的保证两边的大小平衡,所以有一个key值,使他满足堆得性质,来维护树的平衡,key值 ...

  4. FHQ Treap摘要

    原理 以随机数维护平衡,使树高期望为logn级别 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 因此可持久化 先介绍变量 ; int n; struct Node { int v ...

  5. 在平衡树的海洋中畅游(四)——FHQ Treap

    Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...

  6. fhq treap 学习笔记

    序 今天心血来潮,来学习一下fhq treap(其实原因是本校有个OIer名叫fh,当然不是我) 简介 fhq treap 学名好像是"非旋转式treap及可持久化"...听上去怪 ...

  7. 并不对劲的fhq treap

    听说很对劲的太刀流不止会splay一种平衡树,并不对劲的片手流为了反驳他,并与之针锋相对,决定学学高端操作. 很对劲的太刀流-> 据说splay常数极大,但是由于只知道splay一种平衡树能对序 ...

  8. 简析平衡树(四)——FHQ Treap

    前言 好久没码过平衡树了! 这次在闪指导的指导下学会了\(FHQ\ Treap\),一方面是因为听说它可以可持久化,另一方面则是因为听说它是真的好写. 简介 \(FHQ\ Treap\),又称作非旋\ ...

  9. 平衡树(Splay、fhq Treap)

    Splay Splay(伸展树)是一种二叉搜索树. 其复杂度为均摊\(O(n\log n)\),所以并不可以可持久化. Splay的核心操作有两个:rotate和splay. pushup: 上传信息 ...

随机推荐

  1. okhttp在https连接中出现java.net.ProtocolException: Expected ':status' header not present的解决办法

    将版本升级到 com.squareup.okhttp3:okhttp:3.9.0可以解决.

  2. CSS规范 - 代码格式--(来自网易)

    选择器.属性和值都使用小写 在xhtml标准中规定了所有标签.属性和值都小写,CSS也是如此.单行写完一个选择器定义 便于选择器的寻找和阅读,也便于插入新选择器和编辑,便于模块等的识别.去除多余空格, ...

  3. AngularJs-$parsers自我理解-解析

    $parsers 首先先了解下它具体的作用,当用户与控制器进行交互的时候.ngModelController中的$setViewValue()方法就会被调用,$parsers的数组中函数就会以流水线的 ...

  4. 玩转Hook——Android权限管理功能探讨(一)

    随着Android设备上的隐私安全问题越来越被公众重视,恶意软件对用户隐私,尤其是对电话.短信等私密信息的威胁日益突出,各大主流安全软件均推出了自己的隐私行为监控功能,在root情况下能有效防止恶意软 ...

  5. tf.Session()和tf.InteractiveSession()的区别

    官方tutorial是这么说的: The only difference with a regular Session is that an InteractiveSession installs i ...

  6. js星星评分插件

    下载:https://files.cnblogs.com/files/wordblog/%E6%98%9F%E6%98%9F%E6%8F%92%E4%BB%B6.rar

  7. CentOS 无法通过 yum 安装新版 nodejs 解决办法(安装的还是老版的)

    官网安装说明:CentOS 安装 nodejs 第一步: curl --silent --location https://rpm.nodesource.com/setup_10.x | sudo b ...

  8. Android getScrollX()详解

    在开发中相信大家在自定义View时会时不时的使用getScrollX()方法,为了便于之后的开发工作,本篇博客主要记录了我对getScrollX()方法的理解. getScrollX:Return t ...

  9. 分模块开发创建dao子模块——(七)

    1.选中父工程右键新建maven module

  10. 【密码学】RSA算法过程-求解密钥

    1.密钥的计算获取过程 密钥的计算过程为:首先选择两个质数p和q,令n=p*q. 令k=ϕ(n)=(p−1)(q−1),原理见2的分析 选择任意整数d,保证其与k互质 取整数e,使得[de]k=[1] ...