• 算法学习——从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

  • 相关阅读:
    软件工程系统开发课堂测试01
    java--printf
    java--局部类只能访问外包方法的final局部成员
    java--内部类实现“类的多重继承”
    java--内部类
    【转】java--final
    java--实例成员 & 静态成员
    java--方法和成员的继承,访问
    Java--格式化输出
    HDOJ 1005
  • 原文地址:https://www.cnblogs.com/Acheing/p/6911922.html
Copyright © 2020-2023  润新知