技能树

题目描述

“OI 的学习,理应循序渐进”。

OI 中技能的加点形如一棵以 $1$ 为根的树形结构,每个点的父亲就是它的前置知识,比如“min25 筛”应该在“莫比乌斯反演”之后学习,“主席树”应该在“树状数组”之后学习,形式化的来说,对于树上每个节点 $u$ ,必须先学前置知识,也就是它在技能树上父亲 $fa(u)$ 。

学习知识的过程可能是痛苦的,也可能是愉悦的,学习 $i$ 号知识需要消耗 $a_i$ 点愉悦度,学完之后因为满满的成就感会增加 $b_i$ 点愉悦度,注意,此处先结算 $a_i$ 后结算 $b_i$ ,二者并非同一时刻结算。

现在,我们已经学习完了 $1$ 号知识点,并拥有了一个未知的初始愉悦度,因为我们不希望学习过程中出现过分痛苦的情况,所以我们想知道:至少需要多少点初始愉悦度,使得能通过合理的学习顺序学习完技能树上所有知识,并且学习过程中任意时刻愉悦度都不为负?

数据范围

$n \le 10^5,0 \le a_i,b_i \le 10^9$

题解

可以先考虑序列上如何操作。

首先我们应该选择那些 $a_i \le b_i$ 的点,对于这些点我们应先选择 $a_i$ 更小的。

对于那些 $a_i > b_i$ 的点,可以假定 $i$ 比 $j$ 更优,然后列出不等式,发现满足这个的条件是 $b_i>b_j$ ,所以从 $b_i$ 大的开始选即可。

考虑在一棵树上,因为有父亲的限制,所以不能像上述贪心,我们发现如果有一个优先级最高的点,如果它的父亲被选了,那下一个一定会选它,所以维护优先队列,每次选择优先级最高的,跟当前父亲合并即可,这个可以用并查集实现。

代码

#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;
}

 

posted @ 2019-11-06 19:43  xjqxjq  阅读(233)  评论(0编辑  收藏  举报