Problem H. Monster HunterTime 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).
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
#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 q;
std::vector g[N];
int f[N],fa[N],n;
void dfs(int x,int father)
{
for(int i=0;i>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>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>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<