虚树 virtual-tree

我们发现,如果一棵树中真正需要处理的点很少,而总共点数很多时,可以只处理那些需要的点,而忽略其他点。

因此我们可以根据那些需要的点构建虚树,只保留关键点。

oi-wiki上对虚树的介绍

我们根据一下方式建立虚树:

  • 现将所有需要处理的关键按照欧拉序排序。

  • 用一个栈维护一条从根节点到上一个处理过个点的链(虚树上的链)。

  • 考虑将一个新的点加入虚树:

    • 求出这个点与栈顶元素的 \(Lca\) 。

    • 如果 \(Lca\) 不是栈顶元素:

      • 在栈中只保留栈中的链与现在加入点所在的链的公共部分加上一个上一次处理完的链中元素(通过 \(Lca\) 的 \(dfn\) )。

      • 如果 \(Lca\) 已经在栈中,则弹出那个多余的元素。

      • 如果 \(Lca\) 还不在栈中,则将 \(Lca\) 与多余元素连边,并加入 \(Lca\) 。

    • 把新的点加入栈中。

    • 处理完后把栈中的链也连边连上。

注意:由于整个图需要用到的边与点很少,所以在每次新建虚树的时候不能全局清空,而是在把一个新的点加入栈中的时候清空这个点连过的边。

建立虚树代码:

inline void build()
{
	 sort(query+1,query+m+1,cmp),tot=tp=0;
	 sta[++tp]=1,hea[1]=0;
	 for(int i=1,l;i<=m;i++)
	 {
	 	 if(query[i]==1) continue;
	 	 l=Lca(query[i],sta[tp]);
	 	 if(l!=sta[tp])
	 	 {
	 	 	 while(dfn[l]<dfn[sta[tp-1]])
			 {
			 	 Lca(sta[tp-1],sta[tp]); // 这里用来处理这一条虚树中的边的权值。
				 add(sta[tp-1],sta[tp],minn);
				 tp--;
			 }
	 	 	 if(sta[tp-1]!=l)
			 {
			 	 hea[l]=0;
				 Lca(l,sta[tp]); // 这里用来处理这一条虚树中的边的权值。
				 add(l,sta[tp],minn);
				 sta[tp]=l;
			 }
	 	 	 else
			 {
			 	 Lca(l,sta[tp]); // 这里用来处理这一条虚树中的边的权值。
				 add(l,sta[tp--],minn);
			 }
		 }
		 hea[query[i]]=0,sta[++tp]=query[i];
	 }
	 for(int i=1;i<tp;i++)
	 {
	 	 Lca(sta[i],sta[i+1]); // 这里用来处理这一条虚树中的边的权值。
		 add(sta[i],sta[i+1],minn);
	 }
}

建立完之后就可以在虚树上处理答案了,这样即使多次询问,复杂度也是和选中关键点个数同阶的。

例题:

上一篇:设计模式- 开闭原则


下一篇:RAID5阵列掉盘显示未初始化---解决过程