zoukankan      html  css  js  c++  java
  • [luogu]P1084 疫情控制

    原题链接:P1084 疫情控制

    题意

    给定一棵带权树,$1$号点为根节点,某些点上有军队。

    现在要求移动这些军队,使军队覆盖所有的叶子节点,求移动距离最大值的最小值。

    分析

    很难直接求出最小值,我们可以考虑二分这个最小值,让原问题转化为判定问题。

    二分最小值,我们只需要判断能否在$mid$距离内使军队全部移动到覆盖所有的叶子点。

    1.上移军队

    一个军队往哪个方向移动贡献最大?

    明显是往根节点方向移动。

    所以很明显我们第一步就是要把所有的节点尽可能地往上移动。

    如果移动到顶(处于根节点的儿子节点),我们就记录下这个节点的余力(剩下还能走的距离),并且暂时保存并在途中删除这个点(留作备用)。

    保存的时候记录一个点的来源,余力。

    如何快速上移呢?

    倍增预处理出上移$2^k$代祖先的移动距离,可以优化掉一个$log$。

     2.判断还要堵死哪些子树

    然后跑一遍dfs,找出所有未被封死的子树,我们考虑怎么通过移动剩下节点去堵死它们。

    3.移动军队堵死子树

    我们把军队的余力和根节点到各子树的距离按从小到大排序。

    考虑扫一遍并且考虑怎么堵。

    我们考虑一支军队怎么发挥它的最大能力呢?

    首先一支军队堵死它的子树是不需要考虑余力的。

    从小到大扫一遍军队,如果发现军队所在的那棵子树没被堵死,那么直接堵上去就可以了。

    为什么这样是正确的呢?

    越强的军队应该堵死越远的子树,因为我们是从小到大扫军队的,所以这个军队肯定要堵死最近的那个子树。

    但是它却能超越自己的能力堵死更远的子树,何乐而不为呢?

    如果自己所在的那棵子树被堵死了,那么我们从小到大扫一遍,看看还有没有子树可以堵死,有就堵上去。

    这样就可以判断能不能在规定时间里堵死所有子树了。

    实现

    虽然分析的是头头是道,但是这道题的实现却是没有一点头绪。

    1.预处理部分

    预处理部分我们可以参照倍增求LCA的算法一遍O(n)求出所有预处理数组。

    并且我们还可以直接求出一个点到根节点的距离,这样子在后面二分的时候可以直接判断这个点能否进行子树间转移。

    2.上移军队

    对于不能移出子树的点。

    从大到小扫一遍上移的祖先数,如果没有移到根节点或是没有超出能力范围,就把军队上调。

    为每一个点开一个vis数组,记录这个点是否被堵死。

    对于可以移出子树的点。

    我们直接新开结构体数组保存它的来源,剩余能力(mid-dis[i])。

    然后从小到大排序一遍,同时从根开始dfs一遍,如果有子树没被堵死,另开一个结构体数组保存子树到根的距离和子树的编号。

    发现这两个结构体数组本质上是一样的。

    3.子树间转移

    双指针,$i$指针是军队指针,$j$指针是子树指针。

    对于每一个军队,我们先开能否堵死它所在的子树,如果可以,就直接堵死。

    不可以的话,$j$指针往后移,直到找到第一个没被堵死且可以被他堵死的,如果不存在(当前子树比军队大且没被堵死),直接$continue$。

    所有的军队都考虑完之后,再后移一遍$j$指针。

    如果全部子树都被包含,就$return true$,否则$return false$。

    至此,算法结束。

  • 相关阅读:
    [HNOI2004]L语言
    [TJOI2018]异或
    如何定位低效SQL?
    索引失效的情况有哪些?
    trace的作用?
    show profile的作用?
    索引的使用原则
    MySQL主从复制的步骤
    什么是聚簇索引
    什么是全文索引?
  • 原文地址:https://www.cnblogs.com/onglublog/p/10055174.html
Copyright © 2011-2022 走看看