P7011
简述题意
给定一棵树,每个点有一个权值
询问从
Solution
一道经典题了,唯一的印象就是去年 hehezhou 讲过,但是当时还太菜听不懂,现在回来补一下。
考虑直接做 DP ,设
至于判断是否能到达
- 取出当前
g_{u} 中x 最小的那个二元组(x,y) ,如果HP>x 就结束,否则让HP \leftarrow HP+y ,并删掉二元组(x,y) - 重复上述步骤
最后得到的
这样子虽然查询一次的复杂度变高了,但是合并就方便了,考虑如何合并两颗子树
那么现在要把
-
a_{u} > 0 直接往当前集合中丢入一个
(0,a_{u}) -
a_{u}<0 我们令
cur 表示以当前HP 状态下能额外提供的血量为cur ,L 表示如果要进入子树u ,初始的HP 至少应该为多少。- 初始令
cur=a_{u},L=-a_{u} ,表示初始至少有-a_{u} 的血才能进入,能够额外得到a_{u} 的血量。 - 取出最小的二元组
(x,y) - 如果
x\leq L \or cur < 0 ,就令L\leftarrow\max(L,x-cur),cur\leftarrow cur+y - 重复上述操作直到集合为空或者不满足条件
- 如果最后
cur>0 ,就往集合中加入二元组(L,cur)
这样做是考虑我们初始的状态对这个集合的影响。
如果
L\geq x ,那么二元组(x,y) 一定会被统计上,同时如果初始HP 在[x,L) 之间则不应该得到二元组(x,y) 的贡献,因此直接将(x,y) 附加到初始状态上,变成一个新的子问题。如果
cur<0 ,光拿HP\geq L 的这一部分一定不优,肯定是还拿了集合里的其他二元组,因此继续将二元组附加到初始状态上直到cur\geq 0 为止。 - 初始令
可以发现我们每次查询的都是集合里最小值,因此可以用堆来维护,最后只需要查询一下
如果使用左偏树来维护堆是
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();
}