zoukankan      html  css  js  c++  java
  • 算法学习——从bzoj2286开始的虚树学习生活

    【原创】转载请标明原作者~

    http://www.cnblogs.com/Acheing/

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2286

    Description

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    Input

    第一行一个整数n,代表岛屿数量。

    接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n1<=c<=100000

    n+1行,一个整数m,代表敌方机器能使用的次数。

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    Output

    输出有m行,分别代表每次任务的最小代价。

    Sample Input

    10
    1 5 13
    1 9 6
    2 1 19
    2 4 8
    2 3 91
    5 6 8
    7 5 4
    7 8 31
    10 7 9
    3
    2 10 6
    4 5 7 8 3
    3 9 4 6

    Sample Output

    12
    32
    22

    HINT

     对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

    嗯。。无意中看到了这个题。。

    然后。。就走上了一条不归路。。

    首先,很方便就想到个暴力DP

    f[i][0]表示以i为根的子树,所有关键点都被切断的最小代价。

    f[i][1]表示以i为根的子树,与i相连的点未切断的最小代价。

    然后,就发现这个DP可以写成这样:

    f[i]为以i为根的子树,所有i的父亲的关键点都被切断的最小代价。

    那么转移方程就是f[i]=min(v[i],∑f[j]);j为i的儿子

    于是乎。。

    这个DP就变成n^2的了。。

    然后,虚树就上场了!!!!

    虚树是什么呢?

    我发现再看一下这道题

    发现有很多节点都是没有的!

    例如样例中第一个问题,2,3,4,7,8,9号点都对答案没有影响。

    (为什么7号点也没有影响?因为5-9的路径中选的边权(如果要选)肯定选4)

    于是我们可以把原来的树缩成——

    是的,虚树的用途就是将一棵树变成一棵新的树

    且这棵树能包含所有需要的信息

    ---------------------------------------------------------------

    but

    怎么建虚树呢?

    (以下将询问节点称为关键点)

    1、将关键点按dfs序排序

    2、将一个不会成为询问节点的点设为虚树的根节点,再将根节点放入栈中

    3、将排好序的关键点依次加入栈中,然后。。

      1)设当前点为x,栈顶元素为p,q为lca(x,p)

      2)若q=p,则将p加入栈中(为什么q!=x?如果q==x则dfn[x]<dfn[p]然而因为排序过了所以dfn[x]必定小于dfn[p])

      3)若q!=p,说明x和p在两棵子树中。。则再次分类讨论。。(设dfn[i]为i的dfs序号,y为栈顶下面那个元素,即如果进行一次弹出操作,y为栈顶)

        1】若dfn[y]>dfn[q]说明q还在y上面,则在虚树中add(p,y)再进行一次弹出操作。

        2】若dfn[y]=dfn[q]说明y已经是lca了,则直接在虚树中add(p,y)再进行一次弹出操作,然后。。就直接跳到4)就行了。

        3】若dfn[y]<dfn[q]说明!!y已经在q上面了(待会会给出例子),就add(q,p),弹出一次,再将q加入栈中。

      4)然后将x加入栈中。重复以上操作即可

    这样构建出来的虚树,节点一定是最少的

    虚树上(x,y)的边权就是(x,y)在原树上的路径中边权的最小值,可以用树链剖分或倍增来维护。

    ---------------------------------------------------------------

     再画些图~

    啊哦有点大T_T

    反正就是这样,建完虚树后,就可以直接DP了,是不是超神奇!

    写在最后

    1、一棵树上k个点的lca最多只有k-1个,所以一次DP复杂度是O(k)的,空间也是(但要*2!)。

    2、归纳一下,只有在非关键点不影响结果的情况下才能用虚树。

    3、其实树的形态不固定也可以建虚树。

    4、有些证明什么的。。可以去看xyz大爷的论文

    5、至于代码。。

    然后,就完结了!

    难得写这么认真。。

    参考资料:http://www.cnblogs.com/chenhuan001/p/5639482.html

  • 相关阅读:
    带你玩转Flink流批一体分布式实时处理引擎
    都2022年了,你的前端工具集应该有vueuse
    云图说|图解DGC:基于华为智能数据湖解决方案的一体化数据治理平台
    面试官: Flink双流JOIN了解吗? 简单说说其实现原理
    4种Spring Boot 实现通用 Auth 认证方式
    这8个JS 新功能,你应该去尝试一下
    Scrum Master需要具备哪些能力和经验
    dart系列之:时间你慢点走,我要在dart中抓住你
    dart系列之:数学什么的就是小意思,看我dart如何玩转它
    dart系列之:还在为编码解码而烦恼吗?用dart试试
  • 原文地址:https://www.cnblogs.com/Acheing/p/6911922.html
Copyright © 2011-2022 走看看