HDU 6326 2018HDU多校赛 第三场 Monster Hunter(贪心+并查集+优先队列)

Problem H. Monster Hunter

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 318    Accepted Submission(s): 79


 

Problem Description

Little Q is fighting against scary monsters in the game ``Monster Hunter''. The battlefield consists of n intersections, labeled by 1,2,...,n , connected by n−1 bidirectional roads. Little Q is now at the 1 -th intersection, with X units of health point(HP).
There is a monster at each intersection except 1 . When Little Q moves to the k -th intersection, he must battle with the monster at the k -th intersection. During the battle, he will lose ai units of HP. And when he finally beats the monster, he will be awarded bi units of HP. Note that when HP becomes negative(<0 ), the game will over, so never let this happen. There is no need to have a battle at the same intersection twice because monsters do not have extra life.
When all monsters are cleared, Little Q will win the game. Please write a program to compute the minimum initial HP that can lead to victory.

 

Input

The first line of the input contains an integer T(1≤T≤2000) , denoting the number of test cases.
In each test case, there is one integer n(2≤n≤100000) in the first line, denoting the number of intersections.
For the next n−1 lines, each line contains two integers ai,bi(0≤ai,bi≤109) , describing monsters at the 2,3,...,n -th intersection.
For the next n−1 lines, each line contains two integers u and v , denoting a bidirectional road between the u -th intersection and the v -th intersection.
It is guaranteed that ∑n≤106 .  

Output

For each test case, print a single line containing an integer, denoting the minimum initial HP.

 

Sample Input

 

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

 

Sample Output

 

3

 

Source

2018 Multi-University Training Contest 3

大致题意:有N个怪物,然后按照树的形式排列,如果你要打某一个怪物,首先要把它的父亲打掉。你打第i个怪物会扣ai滴血,消灭之后系统会奖励你bi 滴血,现在问你初始的时候至少需要多少血才能够保证,在最优的打怪顺序下,中途的血量不低于0。

本题肯定是存在一组解使得中途血量不低于0的,于是考虑贪心这个打怪的顺序。首先,如果两个怪物一个打下来血量会减少,一个会增加,那么我肯定是先打血量增多那个。其次,都是增加的时候,考虑先打消耗少的。然后我们再考虑如果两个都是减少的时候怎么判断。我们考虑最后一个怪物,它的HP奖励是没有用的,也即后面不会再利用这些奖励的HP,所以我们想要做的肯定是使得这个浪费的HP尽量的少,所以当两个怪物都是亏血的时候,我们优先打那些回血多的。

有了这样的一个顺序之后,我们就可以考虑按照顺序的打怪了。但是,又有这个树的限制,我们考虑用类似石子合并的方法,每次先选择一个优先级最高的怪物,然后把它与它的父亲合并成一个新的怪物。然后再在剩余的n-1个(包括这个新合并的怪物)中找个优先级最高的重复,直到最后只剩下一个怪物。

那么,这个合并的代价怎么计算呢?显然,合并的时候,第二个怪物可以利用第一个怪物打完之后奖励的HP,如果这个HP不够用,那么相当于合并的怪物所需要的初始HP要在第一个怪物的基础上增加他们两个的差值,如果这个HP够用,那么把这个剩余量加到第一个怪物的奖励HP中,组成新怪物的奖励HP。

然后具体做法的话,就是用一个优先队列模拟堆的过程,然后每次并查集进行合并,所有的点都往根上合并,直到最后只剩下一个根节点,最后这个根节点所需要的HP,就是最后的答案。具体见代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define mod 1000000007
#define N 200010
#define LL long long

using namespace std;

struct node
{
    LL a,b,id;

    bool operator < (const node x) const
    {
        if (b-a>=0&&x.b-x.a<0) return false;
        if (b-a<0&&x.b-x.a>=0) return true;
        return b-a>0?a>x.a:b<x.b;
    }

    bool operator != (const node x) const
    {
        return a!=x.a || b!=x.b;
    }

} a[N];

priority_queue<node> q;
std::vector<int> g[N];
int f[N],fa[N],n;

void dfs(int x,int father)
{
    for(int i=0;i<g[x].size();i++)
    {
        int y=g[x][i];
        if (y==father) continue;
        fa[y]=x; dfs(y,x);
    }
}

int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}

int main()
{
    IO;
    int T; cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1;i<=n;i++) g[i].clear();
        a[1]={0,0,1}; fa[1]=f[1]=1;
        for(int i=1;i<n;i++)
        {
            cin>>a[i+1].a>>a[i+1].b;
            a[i+1].id=i+1; f[i+1]=i+1;
            q.push(a[i+1]);
        }
        for(int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(1,0);
        while(!q.empty())
        {
            int x=q.top().id;
            if (q.top()!=a[x]||x==1) {q.pop();continue;}
            int y=find(fa[x]); f[x]=y; q.pop();
            a[y].a+=max(0LL,a[x].a-a[y].b);
            a[y].b=a[x].b+max(0LL,a[y].b-a[x].a);
            q.push(a[y]);
        }
        cout<<a[1].a<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/81319464