虚树学习笔记
ycz去年省选前就会了
虚树应该是一种算法,我们假如有\(k\)个关键点,然后我们要剖出包含这\(k\)个点的一棵树,其中祖先关系并不会改变。
单调栈建虚树好像简单一点
我们栈里面第一个数是\(1\)号节点。然后我们单调栈里面维护的是虚树的一条链。如果新的一个点和链底\(lca\)是链底,那么就\(push\)进去,否则就弹出节点然后把这个过程重复到能加入单调栈为止。自己发明的虚树完全就是假的,搞笑!
我们每次和\(stk[top]\)取\(lca\),分讨
- \(lca=stk[top]\),这个点还在链上
- \(if(id[lca] > id[stk[top-1]])\)
那么就把\(stk[top]\)和\(stk[top-1]\)连一下,然后把\(lca\)和\(u\)都插入即可 - \(if(id[lca]==id[stk[top-1]])\)
同上,就是不用插入\(lca\) - 否则就弹出这个东西并连边直到符合上面符合条件为止
[P2495]
对于每一个询问都建虚树。然后树形dp就做完了
\(dp[u]\)代表是否切断这个子树内的最小代价。
if(to为关键点) dp[u]+=w
else dp[u]+=min(w,dp[to])
[P4103]
处理出\(dep\)然后就可以树形dp了
具体而言,我们\(dep\)先预处理好,然后对于路径的长度和。我们维护三个东西,一个是\(siz,sum_dep,dp\)。
\(dp[u]+=sum[u]*siz[to]+sum[to]*siz[u]-2*siz[u]*siz[to]*dep[u]\)
其他都容易
[P3233]
这东西貌似很有名,谔谔了。
对于\(O(nq)\)的做法,我们直接换根\(dp\),\(dp1\)为子树里到这个最近的,\(dp2\)为父亲到这里最近的。
建出虚树有什么用吗?
我们还是可以处理出这个东西,发现貌似会有一些子树会什么都没有。放到原树上去考虑,发现如果一个点的某一个子树没有关键点,那么子树里的\(dp\)都等于这个点的\(dp\)那我们。经过一点手模发现,首先关键点子树内的点肯定属于,然后我们看看到他的\(lca\)是什么情况,如果\(lca\)是这个\(to\)覆盖的,那么就有机会往上延伸和。哦,所以我们先找出\(lca\)是谁覆盖的,然后判断一下即可。也就是说我们这个点为\(to\),往上倍增找到最后一个点,然后加一下,其他的点都是这样,然后我们每一条边都这样算,然后可能要写倍增求和。可能是假的。
考虑怎么算这个贡献。我们如果对\(u\)->\(to\)这条边算贡献,设\(u\)被\(x\)贡献到了。我们就从\(u\)往上倍增计算最后到达的点是哪一个。然后我们的图就是一些边断掉了,然后就是怎么快速算这个东西了,发现一条链可以这么算,这个贡献直接在顶部加上一个\(siz\),然后在底部把\(siz\)减掉即可,也就是我们往上跳,很对啊。