首页 > 其他分享 >小学二年级都能看懂的 虚树学习笔记

小学二年级都能看懂的 虚树学习笔记

时间:2022-09-27 20:58:04浏览次数:89  
标签:构建 top stk dfn 二年级 能看懂 节点 虚树

过不去那道题?
事实:它数据范围很大
而且每一次询问都得O(n)暴力
出题人最坏了!
介绍……虚树!

介绍

先看这么一道题

P2495 [SDOI2011] 消耗战

题目大意:每一次给k个特殊点,每次可以删除多条边,代价为边权和。求让这些特殊点与根节点不联通的最小代价

这道题的暴力dp非常好想:令 mi[i] 表示从该点到根节点的路径中最小的边权,则转移方程为:

\[dp[x]=min(\sum_{son[x]}^{}mi[son[x]],mi[x]) \]

时间复杂度 O(nm) ,爆炸

我们不难发现,对于答案有影响的只有特殊点以及他们之间的LCA,由于 \(∑k i≤5\times 10^5\) ,理论上是可以过的。这时候我们只取这些点构建一个树,构建的这棵树就被叫做虚树

构建方式

构建方式比较难想,我就不写思路直接写方式了

我们将所有特殊点按照搜索序排序(假设为dfn),并考虑对于虚树内的节点维护一个dfn递增的单调栈,且保证栈内每个点dfn均小于还未处理的特殊点。

我们先将点1压入节点。假设此时处理到了特殊点x,从栈顶取出元素top,并与x求lca,假设为l。由于l为x与top的祖先(即dfn[l]<=dfn[top]),且我们保证dfn[l]<dfn[x],所以一共有两种情况:


1.dfn[l]<dfn[top] 由于dfn[x]<dfn[top],说明x、所有剩下需要处理的点也top并不在l的同一棵子树上,也就是说top所在的那颗子树我们已经构建完毕了,我们尝试去构建它

由于对于栈内每一个节点y,由于我们在构建y时,他的祖先所有dfn更小的子树都被构建完成了,所以对于stk[i],stk[i-1]必然为其虚树上的父亲。换言之,栈内的节点依次连边,构成了虚树上的最右链
(以上可能比较绕,配合图片可能好理解一点,可惜我蒟蒻不会画图QAQ,可以拿样例自己画着试一试)

既然知道了这点我们就很好做了:对于栈顶的前两个元素stk[i]和stk[i-1],如果dfn[stk[i-1]]>=dfn[l],就说明stk[i-1]和stk[i]都在l的子树上,直接把他们连边,再让栈顶出栈就好了,一直到dfn[stk[i-1]]<dfn[l]

这是我们又碰到了一个问题:这时候栈顶top的dfn还是小于等于l的,也就是说top有可能在l的子树上。这时候该怎么办呢?分两种情况讨论:

如果l==top,那什么操作都不用做

如果dfn[l]<dfn[top],说明l在top与top-1的中间。那么我们将l与top连边,然后将top出栈,l入栈即可。

以上我们讨论了dfn[l]<dfn[top],如果l=top就更简单了,说明虚树内top就是x的父亲,x就在最右链上,直接把x入栈即可

代码:


sort(p+1,p+m+1,cmp);
stk[top=1]=1;//将1入栈 
for(int i=1;i<=m;i++){
	int x=p[i];
	if(x==1) continue;
	if(top==1){//为了保证根节点为1,需要保证全程top>=1 
		stk[++top]=x;
		continue;
	}
	int l=lca(stk[top],x);
        /*if(l==top)
		continue;
		对例题的特殊处理,如果x的祖先本来就在虚树上,x就没有加入的必要了
		但建普通虚树时就不必加上这句 
	*/
	while(top>1&&dfn[stk[top-1]]>=dfn[l]){
		//由于dfn[stk[top-1]]<dfn[top]<=dfn[l],所以我们不需要额外判断l==top 
		sadd(stk[top-1],stk[top]);//建边 
		top--;
	}
	if(dfn[l]<dfn[stk[top]]&&top>1){ 
		sadd(l,stk[top]);
		stk[top]=l;//等价于栈顶出栈l入栈 
	}
	stk[++top]=x;
}
for(;top>0;top--){
	//别忘了把最右链上剩下的点加进虚树 
	sadd(stk[top-1],stk[top]);
}

最后在虚树上跑一遍算法就好了

小结

难的不是建出虚树,而是在构建出虚树后怎么用它。比如这道P3233 [HNOI2014]世界树。即使构建除了世界树,仍然需要分类讨论虚树上的子节点、虚树间的链、虚树上的节点之间比较复杂(恶心)的关系。让我刷到这道题的时候不断怀疑自己直接建虚树是不是假的

不过绝大多数情况下,看到多次询问,涉及节点总数量和单次询问复杂度均在一个可以接受的范围内就大胆构建虚树吧,说不定就蒙对了呢(

标签:构建,top,stk,dfn,二年级,能看懂,节点,虚树
From: https://www.cnblogs.com/WhangZjian/p/16732572.html

相关文章

  • 虚树
    虚树是一种神奇的算法,它的具体思想为在一棵较为庞大的树当中,我们可能并不需要考虑该树上的所有节点,而是只需要考虑其中的一部分的话,我们可以使用这些点组建一棵虚树。虚树......
  • 5 个 Python 有用的 One-Liners,初学者也能看懂
    5个Python有用的One-Liners,初学者也能看懂图片from像素现在我正在学习很多关于编程的知识(使用JavaScript和Python),并且我正在解决网站编码练习中的许多挑战。......
  • 虚树
    一种大树变小树的方法。大概就是只保留题目要求的关键点和其他一些统计答案必须的点,把剩余的所有点从树上砍掉。原理是维护一条最右链(就是我们扫到的最右边的一条链,它左边......
  • 人人都能看懂的卡西欧fx991cnx玩机指南,手把手教你如何利用计算器的漏洞爆机
    专业术语说明你是VerB还是VerC别人问你这个问题的时候不要慌,帮你看你的计算器是Ver几:同时按住shift、7、开机键95次shift第一行后半句即是紧接着可以顺便看看计算......
  • dfs序 括号序 欧拉序 树上莫队 虚树建立 傻傻分不清
    括号序进加一次,出加一次,显然最后得到的序列只有\(2n\)个点。voiddfs(intx){ in[x]=++tot; for(y)dfs(y); out[x]=++tot;}显然我们希求将树上路径表示为区......