虚树 学习笔记
给定一棵背景树,再给定这棵树上的一个点集,那么显然,这个点集里的所有点并上两两 LCA,然后按祖先后代关系连边得到的一棵小树可以保证这个点集里的相对关系。这棵树叫做这个点集生成的虚树。
我们将这个点集按欧拉序排序。那么它们的 LCA 集合显然是所有相邻对的 LCA 的集合,由欧拉序 + ST 表求 LCA 法可知。那么虚树大小是和点集大小同阶的。
给定大小为 m 的点集,我们有 O(mlogm) 的方法求出这个虚树。
我们考虑往集合里加一个根,这样更容易求虚树。一般来说带个根是不影响正确性的,必要时可以手动删除。
我们考虑按照这些点的 dfn 从小到大(dfn 相对大小和欧拉序一样)模拟 dfs 的过程。那么实时维护一个递归栈(栈中都是虚树里的点)。
考虑按 dfn 依次加入点集里的点。考虑任意一步时,栈顶显然是按 dfn 排序的上一个。求出栈顶和当前节点的 LCA(每一步都求 LCA 的话,显然可以充满虚树,这个算法做的只是连边……),分两种情况:
- LCA 等于栈顶。那说明当前节点是栈顶的后代,直接加入递归栈;
- 不等于。那么感性理解可以得到,栈中 LCA 的后代的连边关系已经确定了,就一边弹一边连(注意最靠近 LCA 的那个是 LCA 连向它)。然后如果 LCA 不在栈中的话,就加入。然后再把当前节点加入栈。
最后一边清空栈一边连边。就做完啦。
给份代码 qwq:
bool cmp(int x,int y){return dfn[x]<dfn[y];}
int stk[N],top;
vector<int> son[N+1];
void virtree(vector<int> &v){
sort(v.begin(),v.end(),cmp);
stk[top++]=1;
for(int i=0;i<v.size();i++){
int _lca=lca(stk[top-1],v[i]);
if(_lca!=stk[top-1]){
while(dfn[_lca]<dfn[stk[top-2]])son[stk[top-2]].pb(stk[top-1]),top--;
son[_lca].pb(stk[top-1]),top--;
if(_lca!=stk[top-1])stk[top++]=_lca;
}
stk[top++]=v[i];
}
while(top>=2)son[stk[top-2]].pb(stk[top-1]),top--;
top--;
}
建出虚树之后,可以解决这样的问题:给若干个询问,每个询问给一个树上的点集叫你搞事情。不保证点集大小和询问个数,但是保证 ∑m。一般就建虚树来搞啦,几乎跟在原树上搞一样,可以 DP 啊淀粉质啊啥的。但是有的时候边变成了原树上的直链,需要直链查询啥的,或者有其它的小变化?总之虚树的题的虚树部分都很板子就对啦 qwq。
upd 21.9.20:提供一个更好写(或者说好记)的写法。我们可以预先把所有 lca 加进 vector
里,一起搞。这样就不需要讨论很多情况了(因为所有可能的 lca 都一定已经被加入了,就可以实时连边,不需要有以后存在新点加到某条现有边中点而不敢现在就连边的顾虑),直接用递归栈模拟 dfs,每次就回溯直到栈顶是当前点的祖先,连个边,把当前节点加入栈即可,非常无脑。判祖先用 dfn
和 mxdfn
。话说我用这个写法重新写了 P2495 和 P4103 都 WA 了少数点,找了好久 bug,笑死,最后发现是我现在的马蜂和以前不兼容:以前 N=100000
现在 N=100010
,定义 mxdfn
的时候写成了 N
instead of N+1
就炸了。。。
void virtree(vector<int> &v){
for(int i=0;i<v.size();i++)is[v[i]]=true;
v.pb(1);
sort(v.begin(),v.end(),cmp);
int tmp=v.size();
for(int i=0;i+1<tmp;i++)v.pb(lca(v[i],v[i+1]));
sort(v.begin(),v.end(),cmp);v.resize(unique(v.begin(),v.end())-v.begin());
top=0;
for(int i=0;i<v.size();i++){
int x=v[i];
while(top&&!(dfn[stk[top-1]]<=dfn[x]&&dfn[x]<=mxdfn[stk[top-1]]))top--;
if(top)son[stk[top-1]].pb(x);
stk[top++]=x;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· Winform-耗时操作导致界面渲染滞后
· Phi小模型开发教程:C#使用本地模型Phi视觉模型分析图像,实现图片分类、搜索等功能
· 语音处理 开源项目 EchoSharp
· drools 规则引擎和 solon-flow 哪个好?solon-flow 简明教程