虚树 学习笔记

给定一棵背景树,再给定这棵树上的一个点集,那么显然,这个点集里的所有点并上两两 LCA,然后按祖先后代关系连边得到的一棵小树可以保证这个点集里的相对关系。这棵树叫做这个点集生成的虚树。

我们将这个点集按欧拉序排序。那么它们的 LCA 集合显然是所有相邻对的 LCA 的集合,由欧拉序 + ST 表求 LCA 法可知。那么虚树大小是和点集大小同阶的。

给定大小为 \(m\) 的点集,我们有 \(\mathrm O(m\log m)\) 的方法求出这个虚树。

我们考虑往集合里加一个根,这样更容易求虚树。一般来说带个根是不影响正确性的,必要时可以手动删除。

我们考虑按照这些点的 dfn 从小到大(dfn 相对大小和欧拉序一样)模拟 dfs 的过程。那么实时维护一个递归栈(栈中都是虚树里的点)。

考虑按 dfn 依次加入点集里的点。考虑任意一步时,栈顶显然是按 dfn 排序的上一个。求出栈顶和当前节点的 LCA(每一步都求 LCA 的话,显然可以充满虚树,这个算法做的只是连边……),分两种情况:

  1. LCA 等于栈顶。那说明当前节点是栈顶的后代,直接加入递归栈;
  2. 不等于。那么感性理解可以得到,栈中 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--;
}

建出虚树之后,可以解决这样的问题:给若干个询问,每个询问给一个树上的点集叫你搞事情。不保证点集大小和询问个数,但是保证 \(\sum m\)。一般就建虚树来搞啦,几乎跟在原树上搞一样,可以 DP 啊淀粉质啊啥的。但是有的时候边变成了原树上的直链,需要直链查询啥的,或者有其它的小变化?总之虚树的题的虚树部分都很板子就对啦 qwq。

upd 21.9.20:提供一个更好写(或者说好记)的写法。我们可以预先把所有 lca 加进 vector 里,一起搞。这样就不需要讨论很多情况了(因为所有可能的 lca 都一定已经被加入了,就可以实时连边,不需要有以后存在新点加到某条现有边中点而不敢现在就连边的顾虑),直接用递归栈模拟 dfs,每次就回溯直到栈顶是当前点的祖先,连个边,把当前节点加入栈即可,非常无脑。判祖先用 dfnmxdfn。话说我用这个写法重新写了 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;
	}
}
posted @ 2021-02-04 17:46  ycx060617  阅读(122)  评论(0编辑  收藏  举报