1 条题解
-
0
自动搬运
来自洛谷,原作者为

Krimson
别祈祷搬运于
2025-08-24 22:25:51,当前版本为作者最后更新于2021-11-15 22:03:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
简述题意
给定一棵树,每个点有一个权值 ,每次新经过一个没有到达过的点 则会令当前的 。
询问从 出发能否到达 ,并且任意时刻 。
Solution
一道经典题了,唯一的印象就是去年 hehezhou 讲过,但是当时还太菜听不懂,现在回来补一下。
考虑直接做 DP ,设 表示以 的血量进入子树 中最多可以获得 的血量。
至于判断是否能到达 ,就新加入一个权值为 的点连接在 上,最后看 是否为 即可。
虽然查询快,但是修改是 的,而我们只需要查询一次。
同时,即使使用线段树维护,也不太方便实现两个 数组之间的合并,因为要考虑到可能在两个子树间 “反复横跳” 的情况。
因此,我们尝试用另一种方法来得到 ,设 为若干个二元组 的集合,表示如果当前血量 ,则可以再额外加上 。(这个东西和 的差分数组很像不过并不完全一样)
那么原来的 就变成了这样一个过程:
- 初始令
- 取出当前 中 最小的那个二元组 ,如果 就结束,否则让 ,并删掉二元组
- 重复上述步骤
最后得到的 就是 。
这样子虽然查询一次的复杂度变高了,但是合并就方便了,考虑如何合并两颗子树 ,实际上就是把两个集合给合并起来。
那么现在要把 加入到这个子树合并而成的集合中,该如何操作?
-
直接往当前集合中丢入一个
-
我们令 表示以当前 状态下能额外提供的血量为 , 表示如果要进入子树 ,初始的 至少应该为多少。
- 初始令 ,表示初始至少有 的血才能进入,能够额外得到 的血量。
- 取出最小的二元组
- 如果 ,就令
- 重复上述操作直到集合为空或者不满足条件
- 如果最后 ,就往集合中加入二元组
这样做是考虑我们初始的状态对这个集合的影响。
如果 ,那么二元组 一定会被统计上,同时如果初始 在 之间则不应该得到二元组 的贡献,因此直接将 附加到初始状态上,变成一个新的子问题。
如果 ,光拿 的这一部分一定不优,肯定是还拿了集合里的其他二元组,因此继续将二元组附加到初始状态上直到 为止。
可以发现我们每次查询的都是集合里最小值,因此可以用堆来维护,最后只需要查询一下 。
如果使用左偏树来维护堆是 的,当然直接用 STL 外加启发式合并也可以通过此题,而且比较好写。
Code
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #include<cstring> #include<queue> using namespace std; #define ll long long #define ri int #define pll pair<ll,ll> const int MAXN=2e5+7; const ll inf=1e18; int n,t; ll a[MAXN]; vector<int> g[MAXN]; priority_queue<pll,vector<pll>,greater<pll> > q[MAXN]; void merge(priority_queue<pll,vector<pll>,greater<pll> > &p,priority_queue<pll,vector<pll>,greater<pll> > &q){ if(p.size()<q.size()) p.swap(q); while(!q.empty()) p.push(q.top()),q.pop(); } void dfs(int u,int fa){ for(auto v:g[u]){ if(v==fa) continue; dfs(v,u); merge(q[u],q[v]); } if(a[u]>0) q[u].push((pll){0,a[u]}); if(a[u]<0){ ll cur=a[u],L=-cur; while(!q[u].empty()&&(cur<0||q[u].top().first<(L))) L=max(L,q[u].top().first-cur),cur+=q[u].top().second,q[u].pop(); if(cur>0) q[u].push((pll){L,cur}); } } void work(){ scanf("%d%d",&n,&t); for(ri i=1;i<=n+1;++i) g[i].clear(); for(ri i=1;i<=n;++i) scanf("%lld",a+i); for(ri i=1;i<n;++i){ int u,v;scanf("%d%d",&u,&v); g[u].push_back(v),g[v].push_back(u); } ++n; for(ri i=1;i<=n;++i) while(!q[i].empty()) q[i].pop(); g[n].push_back(t),g[t].push_back(n);a[n]=inf; dfs(1,0); ll hp=0; while(!q[1].empty()&&hp>=q[1].top().first){ hp+=q[1].top().second; q[1].pop(); } if(hp*2>=inf) puts("escaped"); else puts("trapped"); } int main(){ int T;cin>>T;while(T--) work(); }
- 1
信息
- ID
- 6171
- 时间
- 8000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者