浅谈基于 FHQ-Treap 实现的单 log LCT
Little_Zyl · · 算法·理论
前言
最近想学
阅读本文前,请保证已掌握
\texttt{FHQ-Treap} 写 双 \log \texttt{LCT}
首先我们先来看一下
和普通
int merge(int l,int r){
if(!l||!r)return l|r;
if(tr[l].k<tr[r].k)return pushdown(l),rc[l]=merge(rc[l],r),pushup(l),l;
else return pushdown(r),lc[r]=merge(l,lc[r]),pushup(r),r;
}
\text{\textbf{access}}
前面那个
我们发现若当前是按
这时我们需要用到一个叫做 加权
:::align{center}
若点
这个东西可以用概率论算,此处就不详细展开说明了。(其实是我不会)
发现若沿用上一部分的实现,
在这里,我们需要用到
{\text{\textbf{3-way split}}}
所谓
void split(int u,int&l,int&r){
if(!top){
pushdown(u);
l=lc[u],r=rc[u],lc[u]=rc[u]=0;
//只有这有区别
pushup(u);
return ;
}
bool d=st[top--]^tag[u];
pushdown(u);
if(d)r=u,split(lc[u],l,lc[u]);
else l=u,split(rc[u],rc[u],r);
pushup(u);
}
{\text{\textbf{3-way merge}}}
相应的,
在实际实现中,我们也不需要真的算出
int merge(int l,int u,int r){
if(!l&&!r)return lc[u]=rc[u]=0,pushup(u),u;
int d=rd()%(sz[l]+wt[u]+sz[r]);
if(d<sz[l])return pushdown(l),rc[l]=merge(rc[l],u,r),pushup(l),l;
else if(d<sz[l]+wt[u])return lc[u]=l,rc[u]=r,pushup(u),u;
else return pushdown(r),lc[r]=merge(l,u,lc[r]),pushup(r),r;
}
\text{\textbf{findleft}}
显然不能去暴力找了,于是我们对
查询时若是根直接
void updyy(int u){
ly[u]=(lc[u]?ly[lc[u]]:u),ry[u]=(rc[u]?ry[rc[u]]:u);
if(tag[u])swap(ly[u],ry[u]);
}
void pushup(int u){
updyy(u);
sz[u]=sz[lc[u]]+wt[u]+sz[rc[u]];//上文提到的子树权值和
if(lc[u])fa[lc[u]]=u;if(rc[u])fa[rc[u]]=u;
}
void pushdown(int u){
if(!tag[u])return ;
swap(lc[u],rc[u]),tag[u]=0;
if(lc[u])tag[lc[u]]^=1,updyy(lc[u]);
if(rc[u])tag[rc[u]]^=1,updyy(rc[u]);
updyy(u);
}
现在考虑如何设置
定义
由于
复杂度分析
设在一次
先考虑
已知
此时
所以单次
现在考虑
因为
定义全局势能函数为森林中所有节点权值的对数和:
在
化成这样之后的分析就和
还有一部分是跳虚边等其他操作的代价,而这些操作在
综合以上三部分的代价,可得该做法单次
但是该做法的常数巨大,在 P3690 甚至跑不过上面的双
完整代码
以下为 P3690 分别使用这两种做法的所写的代码,以供参考。(码风略丑)
::::success[双
#include<bits/stdc++.h>
using namespace std;
unsigned seed=0x9e3779b9;
unsigned rd(){seed^=seed<<13,seed^=seed>>17,seed^=seed<<5;return seed;}
const int N=3e5+5;
struct LCT{
struct{int v,k;}tr[N];
int lc[N],rc[N],fa[N],fb[N],tag[N],xr[N],st[N],top;
void pushdown(int u){
if(!tag[u])return;
swap(lc[u],rc[u]),tag[u]=0;
tag[lc[u]]^=1,tag[rc[u]]^=1;
}
void pushup(int u){
xr[u]=tr[u].v^xr[lc[u]]^xr[rc[u]];
if(lc[u])fa[lc[u]]=u;if(rc[u])fa[rc[u]]=u;
}
bool isrt(int u){return (lc[fa[u]]!=u&&rc[fa[u]]!=u)||!fa[u];}
int findrt(int u){
for(top=0;!isrt(u);u=fa[u])
st[++top]=(lc[fa[u]]==u);
return u;
}
int findleft(int u){
u=findrt(u),pushdown(u);
for(;lc[u];u=lc[u],pushdown(u));
return u;
}
void split(int u,int&l,int&r){
if(!top)return pushdown(u),l=u,r=rc[u],rc[u]=0,pushup(u),void();
bool d=st[top--]^tag[u];
pushdown(u);
if(d)r=u,split(lc[u],l,lc[u]);
else l=u,split(rc[u],rc[u],r);
pushup(u);
}
int merge(int l,int r){
if(!l||!r)return l|r;
if(tr[l].k<tr[r].k)return pushdown(l),rc[l]=merge(rc[l],r),pushup(l),l;
else return pushdown(r),lc[r]=merge(l,lc[r]),pushup(r),r;
}
int access(int u){
int last=0;
for(int tp,bt;u;u=fb[findleft(last)]){
split(findrt(u),tp,bt);
fb[findleft(last)]=0;
last=merge(tp,last);
fb[findleft(bt)]=u;
}
return last;
}
void makert(int u){tag[access(u)]^=1;}
void link(int u,int v){makert(u),fb[u]=v;}
void cut(int u,int v){makert(u),access(v),access(u),fb[v]=0;}
int query(int u,int v){return makert(u),xr[access(v)];}
void update(int u,int v){tr[u].v=v;for(;!isrt(u);u=fa[u])pushup(u);pushup(u);}
int root(int u){return findleft(access(u));}
bool check(int u,int v){return root(u)==root(v);}
void newnode(int p,int v){
tr[p]={v,(int)rd()},xr[p]=v;
lc[p]=rc[p]=fa[p]=fb[p]=tag[p]=0;
}
}lct;
int n,m;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,v;i<=n;i++)
cin>>v,lct.newnode(i,v);
for(int op,u,v;m--;){
cin>>op>>u>>v;
if(op==0)cout<<lct.query(u,v)<<"\n";
if(op==1&&!lct.check(u,v))lct.link(u,v);
if(op==2&&lct.check(u,v))lct.cut(u,v);
if(op==3)lct.update(u,v);
}
return 0;
}
::::
AC 记录,总耗时 1.06s,最大耗时 437ms。
::::success[单
#include<bits/stdc++.h>
using namespace std;
unsigned seed=0x9e3779b9;
unsigned rd(){seed^=seed<<13,seed^=seed>>17,seed^=seed<<5;return seed;}
const int N=1e5+5;
struct LCT{
int tr[N],lc[N],rc[N],ly[N],ry[N],fa[N],fb[N],tag[N],xr[N],wt[N],sz[N],st[N],top;
void updyy(int u){
ly[u]=(lc[u]?ly[lc[u]]:u),ry[u]=(rc[u]?ry[rc[u]]:u);
if(tag[u])swap(ly[u],ry[u]);
}
void pushup(int u){
updyy(u);
xr[u]=xr[lc[u]]^tr[u]^xr[rc[u]];
sz[u]=sz[lc[u]]+wt[u]+sz[rc[u]];
if(lc[u])fa[lc[u]]=u;if(rc[u])fa[rc[u]]=u;
}
void pushdown(int u){
if(!tag[u])return ;
swap(lc[u],rc[u]),tag[u]=0;
if(lc[u])tag[lc[u]]^=1,updyy(lc[u]);
if(rc[u])tag[rc[u]]^=1,updyy(rc[u]);
updyy(u);
}
bool isrt(int u){return !fa[u]||(lc[fa[u]]!=u&&rc[fa[u]]!=u);}
int findrt(int u){
for(top=0;!isrt(u);u=fa[u])
st[++top]=(lc[fa[u]]==u);
return u;
}
void split(int u,int&l,int&r){
if(!top)pushdown(u),l=lc[u],r=rc[u],lc[u]=rc[u]=0;
else{
bool d=st[top--]^tag[u];
pushdown(u);
if(d)r=u,split(lc[u],l,lc[u]);
else l=u,split(rc[u],rc[u],r);
}
pushup(u);
}
int merge(int l,int u,int r){
if(!l&&!r)return lc[u]=rc[u]=0,pushup(u),u;
int d=rd()%(sz[l]+wt[u]+sz[r]);
if(d<sz[l])return pushdown(l),rc[l]=merge(rc[l],u,r),pushup(l),l;
else if(d<sz[l]+wt[u])return lc[u]=l,rc[u]=r,pushup(u),u;
else return pushdown(r),lc[r]=merge(l,u,lc[r]),pushup(r),r;
}
int access(int u){
int last=0;
for(int tp,bt,tmp;u;u=fb[ly[last]]){
split(findrt(u),tp,bt);
if(tmp=ly[last])fb[tmp]=0,wt[u]-=sz[last];
if(tmp=ly[bt])fb[tmp]=u,wt[u]+=sz[bt];
last=merge(tp,u,last);
}
return last;
}
void makert(int u){int rt=access(u);tag[rt]^=1,updyy(rt);}
void upd(int u){int x,y;split(findrt(u),x,y),merge(x,u,y);}
void link(int u,int v){makert(u),makert(v),fb[v]=u,wt[u]+=sz[findrt(v)],upd(u);}
void cut(int u,int v){
makert(u),access(v),access(u);
if(fb[v])fb[v]=0,wt[u]-=sz[findrt(v)],upd(u);
}
int query(int u,int v){return makert(u),xr[access(v)];}
void update(int u,int v){tr[u]=v;for(;!isrt(u);u=fa[u])pushup(u);pushup(u);}
int root(int u){return ly[findrt(access(u))];}
bool check(int u,int v){return root(u)==root(v);}
void newnode(int p,int v){
tr[p]=xr[p]=v,ly[p]=ry[p]=p,wt[p]=sz[p]=1;
fa[p]=fb[p]=lc[p]=rc[p]=tag[p]=0;
}
}lct;
int n,m;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,v;i<=n;i++)
cin>>v,lct.newnode(i,v);
for(int op,u,v;m--;){
cin>>op>>u>>v;
if(op==0)cout<<lct.query(u,v)<<"\n";
if(op==1&&!lct.check(u,v))lct.link(u,v);
if(op==2&&lct.check(u,v))lct.cut(u,v);
if(op==3)lct.update(u,v);
}
return 0;
}
::::
AC 记录,总耗时 1.28s,最大耗时 584ms。
参考资料
- 《Dynamic Trees with Alternative Search Trees》 by riteme
- 《用无旋转 Treap 来写 LCT》 by immortalCO
- 《FHQ&LCT》 by a1a2a3a4a5
- 《用 Treap 实现的单 log LCT》 by lgswdn_SA
AI 使用说明:本文在复杂度分析部分使用了 Deepseek 和 Gemini 辅助完成,其他内容均由真人完成。
若文章内容有误,欢迎大家指出,我会尽快做出修改 QwQ。