题目描述
“OI 的学习,理应循序渐进”。
OI 中技能的加点形如一棵以 1 1 1 为根的树形结构,每个点的父亲就是它的前置知识,比如“min25 筛”应该在“莫比乌斯反演”之后学习,“主席树”应该在“树状数组”之后学习,形式化的来说,对于树上每个节点 u u u ,必须先学前置知识,也就是它在技能树上父亲 f a ( u ) fa(u) fa(u) 。
学习知识的过程可能是痛苦的,也可能是愉悦的,学习 i i i 号知识需要消耗 a i a_i ai 点愉悦度,学完之后因为满满的成就感会增加 b i b_i bi 点愉悦度,注意,此处先结算 a i a_i ai 后结算 b i b_i bi ,二者并非同一时刻结算。
现在,我们已经学习完了 1 1 1 号知识点,并拥有了一个未知的初始愉悦度,因为我们不希望学习过程中出现过分痛苦的情况,所以我们想知道:至少需要多少点初始愉悦度,使得能通过合理的学习顺序学习完技能树上所有知识,并且学习过程中任意时刻愉悦度都不为负?
数据范围
n ≤ 1 0 5 , 0 ≤ a i , b i ≤ 1 0 9 n \le 10^5,0 \le a_i,b_i \le 10^9 n≤105,0≤ai,bi≤109
题解
可以先考虑序列上如何操作。
首先我们应该选择那些 a i ≤ b i a_i \le b_i ai≤bi 的点,对于这些点我们应先选择 a i a_i ai 更小的。
对于那些 a i > b i a_i > b_i ai>bi 的点,可以假定 i i i 比 j j j 更优,然后列出不等式,发现满足这个的条件是 b i > b j b_i>b_j bi>bj ,所以从 b i b_i bi 大的开始选即可。
考虑在一棵树上,因为有父亲的限制,所以不能像上述贪心,我们发现如果有一个优先级最高的点,如果它的父亲被选了,那下一个一定会选它,所以维护优先队列,每次选择优先级最高的,跟当前父亲合并即可,这个可以用并查集实现。
代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5; LL a[N],b[N];
int n,fa[N],f[N],hd[N],V[N],nx[N],t;
struct O{
int x;
friend bool operator < (const O& A,const O& B){
if (b[A.x]>=a[A.x] && b[B.x]>=a[B.x])
return a[A.x]>a[B.x];
if (b[A.x]<a[A.x] && b[B.x]<a[B.x])
return b[A.x]<b[B.x];
return b[A.x]<a[A.x];
}
};
priority_queue<O>q;
int get(int x){
return f[x]==x?x:f[x]=get(f[x]);
}
void add(int u,int v){
nx[++t]=hd[u];V[hd[u]=t]=v;
}
void dfs(int x,int fr){
fa[f[x]=x]=fr;
for (int i=hd[x];i;i=nx[i])
if (V[i]!=fr) dfs(V[i],x);
}
int main(){
cin>>n;
for (int i=2;i<=n;i++)
scanf("%lld%lld",&a[i],&b[i]),
q.push((O){i});
for (int x,y,i=1;i<n;i++)
scanf("%d%d",&x,&y),
add(x,y),add(y,x);
dfs(1,0);int u,v;LL A,B;
while(!q.empty()){
u=q.top().x;q.pop();
v=get(fa[u]);f[u]=v;
A=max(a[v],a[v]-b[v]+a[u]);
B=b[v]+b[u]-a[v]-a[u]+A;
a[v]=A;b[v]=B;
}
printf("%lld\n",a[1]);return 0;
}