0%

START205——Water Supply

思路讲解

  1. 单条边的传输关系是单调的
    对一条漏水系数为 w 的边,从上游输入 x,到下游得到 x - ⌊x/w⌋;这是关于 x 的非减函数。
    因而“想让下游至少得到 need”的逆运算也是单调:最小的上游输入 x 满足 x - ⌊x/w⌋ ≥ need。你代码里的 get(need, w) 就是在做这件事(通过二分求最小可行 x)。

  2. 一次修边只能选择一条路径上的一条边
    每天只走“根→某个节点”的一条简单路径,因此修哪条边只会影响包含该边的那些路径。
    若我们要求所有**“弱点”节点**(即 R[u] < 目标值 mid 的节点)都至少收到 mid,那么被修的那条边必须同时位于它们各自的从根到该点路径上。
    这些路径的公共部分正是 根→(所有弱点的 LCA) 的路径。所以:

必要条件:可行的被修边一定在 root → anc 这条路径上,其中 anc 是所有 R[u]<mid 的节点的 LCA。

  1. 把“树下游的要求”折算为“对 anc 入水量的要求”
    如果我们暂时不修边,问:“从 anc 向下,把整棵以 anc 为根的子树都喂到 ≥ mid,需要 anc 处至少有多少输入?”
    由于每条边的传输是单调的,这个“所需的 anc 输入量”是单调的,可以二分出最小可行值 need_at_anc
    你用 dfs2(anc, candidate, mid) 来判定“给 anc 值为 candidate 时,子树是否都 ≥ mid”,并对 candidate 二分拿到最小的 need_at_anc。这一步只会遍历 anc 的子树,而不是整棵树,所以可控。

  2. 把 anc 的需求一路往上折算,看是否能借“修一条边”满足
    假设我们已经知道:只要 anc 处能拿到至少 need_at_anc 的输入,它子树就都 OK。
    现在看 root → anc 这条路径。我们在这条路径上自下而上地把需求往上游推

  • 当前在节点 node 处需要 need;让 fa=node 的父亲,边权 w=A[node]
  • 修边选在 (fa,node) 上,则 node 能直接拿到 R[fa](因为这条边不再漏水)。于是只要 R[fa] ≥ need 就已经满足了——这就是你代码里的“只要某一层祖先的 R[ancestor] ≥ need****,我们就能在这个祖先与其子之间修边,一步到位”的判定。
  • 否则,如果暂时不修这条边,就要通过这条漏水边把 need 推到父亲处,父亲的所需输入变为 get(need, w)。继续往上迭代,直到找到某个祖先 y 满足 R[y] ≥ 当前 need,那么在 (y, its child) 修边即可;若最终到根也没有成功,则该 mid 不可行。

这正是你 check(mid) 的核心:

  • 先找弱点集合 wp = { i | R[i] < mid },若空则已可行。

  • anc = LCA(wp 所有点)

  • anc 的所需输入二分最小可行 need_at_anc(用 dfs2 验证)。

  • need_at_anc 沿 anc→root 方向往上折算:

    • 若某处祖先的原始 R[ancestor] ≥ 当前 need → 可在它与子之间修边,成功;
    • 否则用 get(need, w) 把需求继续往上推;
    • 推到顶都不行则失败。

于是,一次修边能否让所有点 ≥ mid 被转换为上述流程的一个布尔判断。

AC代码

AC

https://www.codechef.com/viewsolution/1194966856

心路历程(WA,TLE,MLE……)