HDU 6326 Problem H. Monster Hunter(贪心+优先队列)

Description

有一棵 n n 个节点的树,每个点有一个怪物,打败该怪物消耗血量为 ai a i ,打败怪物后回血为 bi b i ,勇士从 1 1 节点出发开始打怪,只有当勇士血量不低于怪物血量时才能打败怪物,且只有打败父亲节点的怪物才能打当前节点的怪物,问勇士初始最少需要多少血量才能打败所有怪物

Input

第一行一整数 T T 表示用例组数,每组用例首先输入一整数 n n 表示点数,之后 n n 行每行输入两个整数 ai,bi a i , b i 表示打败第 i i 个怪物需要扣掉的血量和打败第 i i 个怪物可以回复的血量,最后 n1 n − 1 行输入 n1 n − 1 条树边

(1T2000,2n105,0ai,bi109,n106) ( 1 ≤ T ≤ 2000 , 2 ≤ n ≤ 10 5 , 0 ≤ a i , b i ≤ 10 9 , ∑ n ≤ 10 6 )

Output

输出勇士打败所有怪物需要的最少初始血量

Sample Input

1
4
2 6
5 4
6 2
1 2
2 3
3 4

Sample Output

3

Solution

先不考虑父亲的限制,那么必然首先选 aibi a i ≤ b i 的怪打,然后再打 ai>bi a i > b i 的怪.

对于 aibi a i ≥ b i 的怪,显然先打 ai a i 较小的可以最小化初始血量,而对于 ai>bi,aj>bj a i > b i , a j > b j 的两个怪,若先打 i i 再打 j j 那么血量的最小值为 HP+min(ai,ai+biaj) H P + m i n ( − a i , − a i + b i − a j ) ,而先打 j j 后打 i i 的血量最小值为 HP+min(aj,aj+bjai) H P + m i n ( − a j , − a j + b j − a i ) ,若 bibj b i ≤ b j ,则由 bibj<aj b i ≤ b j < a j ai+biaj<ai − a i + b i − a j < − a i 以及 ai+biajaj+bjai − a i + b i − a j ≤ − a j + b j − a i ,且由 ai>bi a i > b i ai+biajaj − a i + b i − a j ≤ − a j ,此时 HP+min(ai,ai+biaj)HP+min(aj,aj+bjai) H P + m i n ( − a i , − a i + b i − a j ) ≤ H P + m i n ( − a j , − a j + b j − a i ) ,故对于 ai>bi a i > b i 的应该先打 bi b i 大的怪,以此可以确定打这 n n 个怪的顺序 p1,...,pn p 1 , . . . , p n

之后考虑父亲的限制,假设处理点 pi p i 的时候, pi p i 的父亲已经被处理掉,那么 pi p i 直接处理就行,但若 pi p i 父亲之前没有被处理,那么将 pi p i 与其父亲合并为一个新的节点放入剩下节点中重新确定顺序即可,实现上述操作需要支持插入且保证有序性,可以用一个堆来维护,而将两点合并可以用并查集维护

具体操作为,预处理每点 x x 的父亲节点 pa[x] p a [ x ] ,每次在并查集中 find(pa[x]) f i n d ( p a [ x ] ) 即为找到合并后 x x 的父亲节点,由于在将 v v 和其父亲节点 u u 合并时,无法很快的在堆中删掉 u u ,可以用一个标记数组标记 u u ,之后在 u u 出现在堆顶时直接删去 u u 即可,为方便标记,在 u,v u , v 合并时我们可以把合并后的节点编号依旧记为 v v ,且实际删去 u u 点,把 v v 的所有兄弟变成 v v 的儿子,然后将 pa[v] p a [ v ] 变成 pa[u] p a [ u ] ,这样即可将 u u 的全部信息合并入 v v

注意, (a1,b1) ( a 1 , b 1 ) (a2,b2) ( a 2 , b 2 ) 合并并不是简单的将 a,b a , b 加一起构成一个新的怪物,假如先处理 (a1,b1) ( a 1 , b 1 ) ,那么打第一个怪时需要的最小血量为 a1 a 1 ,打完后剩余血量为 b1 b 1 ,而打第二个怪时需要的血量为 max(a2b1,0) m a x ( a 2 − b 1 , 0 ) ,故总需要血量为 a=a1+max(a2b1,0) a = a 1 + m a x ( a 2 − b 1 , 0 ) ,而打完后剩余血量为 b=b2+max(b1a2,0) b = b 2 + m a x ( b 1 − a 2 , 0 ) ,也即 (a1,b1)+(a2,b2)=(a,b) ( a 1 , b 1 ) + ( a 2 , b 2 ) = ( a , b )

Code

#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define maxn 100005
struct node
{
    ll a,b;
    int id;
    node(){};
    node(ll _a,ll _b,int _id)
    {
        a=_a,b=_b,id=_id;
    }
    bool operator<(const node&x)const
    {
        if(a-b<=0&&x.a-x.b<=0)return a>x.a;
        if(a-b>0&&x.a-x.b>0)return breturn a-b>x.a-x.b;
    }
}m[maxn];
node merge(node x,node y)
{
    return node(x.a+max(y.a-x.b,0ll),y.b+max(x.b-y.a,0ll),y.id);
}
priority_queueque;
int T,n,fa[maxn],pa[maxn],vis[maxn];
vector<int>g[maxn];
void dfs(int u,int f)
{
    pa[u]=f;
    for(int i=0;iint v=g[u][i];
        if(v==f)continue;
        dfs(v,u);
    }
}
int find(int x)
{
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        {
            scanf("%lld%lld",&m[i].a,&m[i].b);
            m[i].id=i;
            que.push(m[i]);
        }
        for(int i=1;i<=n;i++)g[i].clear(),vis[i]=0,fa[i]=i;
        for(int i=1;iint u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v),g[v].push_back(u);
        }   
        dfs(1,1);
        node ans=node(0,0,0);
        vis[1]=1;
        while(!que.empty())
        {
            node now=que.top();
            que.pop();
            if(vis[now.id])continue;
            int v=now.id,u=find(pa[v]);
            if(vis[u])
            {
                ans=merge(ans,now);
                vis[v]=1;
            }
            else
            {
                fa[u]=v;
                pa[v]=pa[u];
                vis[u]=1;
                m[v]=merge(m[u],now);
                que.push(m[v]);
            }
        }
        printf("%lld\n",ans.a);
    }
    return 0;
}

你可能感兴趣的:(HDU,贪心,杂题)