虚树学习

口胡一下怕忘了。应该会有错留坑回来改。

例题:bzoj2286: [Sdoi2011]消耗战 

哔哔一下题意:边有边权。$m$次询问,每次给定$k$个点,割掉若干条边使得$1$号点与给定的点不连通,要求代价最小。

$n \leq 2e5 , \sum k \leq 5e5$

首先这个$\sum k$就很特别。是建虚树的契机。

我们不可能每次跑一遍$O(n)$的树$dp$,那我们能不能每次$O(k)$呢?

做法就是每回只在关键点上做$dp$,就要求我们建出只有关键点的树。即虚树。注意关键点不仅是给定的$k$个点,还有它们所有点两两的LCA。

实际上算上LCA也只有不超过$2k$个点。想一想为什么。(没事,我自己想想而已)

靠全是废话。直接说咋构建。

所有点按dfs序排序。按顺序插入栈中。设栈顶元素为$p$,现在要插入$x$。

栈里维护的是根到$p$的一条链。每插入一个点,和栈顶只有两种关系:

$p$和$x$的LCA是$p$;是另一个点(可能在栈里可能不在,可能是给定点可能不是)。根据dfs序,不存在第三种情况。

第一种情况,直接进栈,注意不用加边。因为可能会出现和之后点的LCA在$p$之下,这种情况下应该是$p$连这个LCA,LCA再连$x$。而这时候这个LCA还不知道是谁甚至有没有。

另一种情况,我们令$LCA(p,x)$为$fa$。这时说明$p$的子树已经遍历完了,不然不会轮到$x$。我们前面说过这个$fa$可在可不在,但一定在栈当前维护的链上。(真tm废话)于是开始弹栈。

我们称栈顶的第二个元素为$q$,如果$q$的深度(这里用dfs序也是一样的道理)大于$fa$,$fa$在$q$之上,$p$与$q$连边,弹出$q$。直到$q$的深度等于或者小于$fa$的,说明$q$就是$fa$或者$fa$在$p,q$之间,$fa,p$连边,$fa$入栈,$p$弹栈(要是$fa=q$就不用了),$x$入栈。

所有点对的LCA我们可以$O(logn)$时间求出,所以所有询问构建虚树的总复杂度就是$O(\sum k logn)$的。听着高大上的东西,其实道理很简单。给我的感觉就是和缩点的$tarjan$算法一样,只是一个工具,难点还是在于$dp$吧。

板子等我熟练了在贴吧。。

 


 

upd

可能就是这样吧 

s[++top]=1;
for(int i=(a[1]==1)+1;i<=k;i++){
    int f=lca(a[i],s[top]);
    if(f==s[top]){
        s[++top]=a[i];continue;
    }
    while(1){
        if(dep[s[top-1]]<=dep[f]){
            add(f,s[top]);
            top--;
            if(s[top]^f)s[++top]=f;
            break;
        }
        add(s[top],s[top-1]);
        top--;
    }
    s[++top]=a[i];
}
while(--top)add(s[top],s[top+1]);

 

转载于:https://www.cnblogs.com/orzzz/p/8232344.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值