LGP11189 [KDOI R10] 水杯降温 学习笔记

LGP11189 [KDOI R10] 水杯降温 学习笔记

\(\texttt{Luogu Link}\)

前言

不拖啦。

题意简述

有一棵 \(n\) 个结点的树,以 \(1\) 为根。每个结点 \(u\) 有权值(水杯),初始为 \(a_u\)

你有两种操作,可进行任意次:

  • 加热:子树全体加 \(1\)
  • 吹风:从某叶子到根直链全体减 \(1\)

问能否通过操作使得最后所有权值均为 \(0\)

\(n\le 10^5\)\(a_i\le 10^{12}\)

做法解析

以及分析这整道题的内容,你会发现:有的是两种“区间”改,最终对某种状态的要求;没有的是区间求和,树路径信息;看起来不太有的是树上 \(\texttt{DP}\) 值的维护。这导向树上差分。

对于父子 \((u,v)\),设 \(d_v=a_v-a_u\),特别地定义 \(d_1=a_1\)。在这个视角下,对 \(u\) 子树全体加,相当于把 \(d_u\) 加一;对 \(u\) 到根直链全体减 \(1\),就是对于这直链上所有的结点 \(u\),对于它们所有不在直链上的儿子 \(v\),将 \(d_v\) 加一。

你容易发现,所有 \(d_u\) 的值全都是只增不减的。这样,无解的判定很简单了:终局是要求所有 \(a_u=0\) 的,这相当于要求 \(a_1=0\) 且所有 \(d_u=0\),因此,若存在 \(d_u>0\) 则无解(此处 \(u\neq 1\))。

进行了上面的判断后,那什么时候有解呢?首先如果初始 \(a_1\le 0\) 那就必然有解,因为我们可以先对着 \(1\) 子树加若干次把 \(a_1\) 加到 \(0\),再对着下面所有 \(d_u<0\) 的结点子树加到 \(d_u=0\)。这是简单的。

相对困难的是 \(a_1>0\) 的情形。这意味着我们必须先对着 \(1\) 吹若干次风,把 \(a_1\) 吹到 \(0\),再对着下面所有 \(d_u<0\) 的结点子树加到 \(d_u=0\)。问题在于,我们这些风要对着哪吹呢?吹风会使“很多”的 \(d_u\) 增大,我们不能让 \(d_u\) 被加到大于 \(0\)

于是,我们设 \(f_u\) 表示,只考虑 \(u\) 的子树,对着这个子树内的点最多可以吹风多少次。显然对于 \(f_u\) 来说 \(f_v\) 都是自己的子问题,所以这个东西从可以从下往上转移。

具体来说,我们来考虑一下 \(f_u\) 取值有什么限制。\(f_u\) 条吹风直链经过 \(u\) 时,对于它的一个儿子 \(v\) 来说,若有 \(g_v\) 条吹风直链经过它,就意味着有 \(f_u-g_v\) 条直链从它旁边擦肩而过。由我们之前的分析,这意味着 \(d_v\) 会被加上 \(f_u-g_v\) 次。那么显然我们要求:\(\forall v\in son[u]\)\(f_u-g_v\le |d_v|\),当然还有 \(g_v\le f_v\)。因此总的限制就是:\(f_u-|d_v|\le g_v\le f_v\)\(\sum g_v\le f_u\)\(f_u\le \sum f_v\)

如果你特别糖,你可能会问:为什么 \(f_u\le \sum f_v\)?难道不能从 \(u\) 吹一些风吗?

确实不能。题目说的是“吹风:从某叶子到根直链全体减 \(1\)”。你是不是把“叶子”两个字看掉了。

显然 \(f_u\) 越小条件越好满足,\(f_u\) 是可二分的。所以二分就完了。

代码实现

Inf 只开了 \(10^{13}\) 而非 \(10^{18}\)。原因是代码中的 fss 是直接加的,如果叶子太多 Inf 又太大可能在二分内或者求 fss 处加爆 long long

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=1e5+5;
const lolo Inf=1e13;
int N,X,tfa[MaxN];
lolo A[MaxN],D[MaxN],F[MaxN],fss[MaxN];
vector<int> Tr[MaxN];
bool check(int u,lolo lim){
    lolo tsum=0;
    for(auto v : Tr[u])tsum+=max(0ll,lim-D[v]);
    return tsum<=lim;
}
void dfs(int u){
    if(!Tr[u].size()){F[u]=Inf;return;}
    fss[u]=0;for(int v : Tr[u])dfs(v),fss[u]+=F[v];
    lolo sl=0,sr=min(Inf,fss[u]),smid,sres=-1;
    for(auto v : Tr[u])minner(sr,F[v]+D[v]);
    while(sl<=sr){
        smid=(sl+sr)>>1;
        if(check(u,smid))sres=smid,sl=smid+1;
        else sr=smid-1;
    }
    F[u]=sres;
}
void mian(){
    readi(N);
    for(int i=1;i<=N;i++)Tr[i].clear();
    for(int i=2;i<=N;i++)readi(tfa[i]),Tr[tfa[i]].push_back(i);
    for(int i=1;i<=N;i++)readi(A[i]);
    for(int i=1;i<=N;i++)D[i]=A[i]-A[tfa[i]];
    for(int i=2;i<=N;i++)if(D[i]>0){puts("Shuiniao");return;}
    for(int i=1;i<=N;i++)D[i]=abs(D[i]);
    if(A[1]<0){puts("Huoyu");return;}
    dfs(1);puts(F[1]>=A[1]?"Huoyu":"Shuiniao");
}
int Tpn,Tcn;
int main(){
    readis(Tpn,Tcn);
    while(Tcn--)mian();
    return 0;
}
posted @ 2025-10-14 11:05  矞龙OrinLoong  阅读(5)  评论(0)    收藏  举报