杭电多校第三场 1008 HDU-6326 Monster Hunter(贪心)

5 篇文章 0 订阅

Problem H. Monster Hunter
Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 384    Accepted Submission(s): 92


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

题目大意:在一棵n个结点的树上除了结点为1的结点之外每个结点都有一个怪兽,每次你打死一只怪兽需要减少ai点HP,同时在打死怪兽之后你可以回复bi点HP。一开始你在结点1,你可以沿着树上的边走到别的结点,现在你要打死所有的怪兽,那么你一开始最少需要有多少点体力。

题目思路:如果可以任意顺序对怪兽进行攻击,考虑贪心,肯定是优先打死ai<bi的怪兽,这样可以获得更多的HP回复,对于所有ai<bi的情况可以按照ai从小到大的顺序打死。对于ai>=bi的情况,如果对于怪兽 i 和怪兽 j ,如果先打 i 所需的最小初始HP为max(ai,ai-bi+aj),如果先打 j 所需的最小初始HP为max(aj,aj-bj+ai),所以考虑两个怪兽的优先攻击时先考虑b的大小。

所以按照贪心策略可以将最优攻击顺序列出来为:p1,p2,...,pn,这个顺序可以借助优先队列来维护。

而题目是在一棵有根树上面,所以我们要攻击某个结点的怪兽的话,肯定要先把其父亲结点的怪兽打死,那么我们就可以考虑将子节点的属性通过并查集合并到父亲结点,此时父亲结点的属性更新为

a[fa]=max(a[fa],a[fa]+a[son]-b[fa])

b[fa]=b[fa]+b[son]-a[fa]-a[son]+a[fa]

然后就可以将子结点删除,将当前子节点的子节点的父节点变为当前子节点的父节点,重复这个过程之后就可以将n个点合并为一个点,就可以得到最后答案了。

具体实现看代码:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+r,rt<<1|1
#define lowbit(x) x&-x
#define pb push_back
#define MP make_pair
#define clr(a) memset(a,0,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
const int MX=100100;

int n,_;
struct node{
	ll a,b;
	int t,id;

	bool operator<(const node&A)const{
		int sign1=(a<b),sing2=(A.a<A.b);
		if(sign1!=sing2) return sign1<sing2;
		if(a<b) return a>A.a;
		return b<A.b;
	}
};
struct edge{int v,nxt;}E[MX<<1];
int head[MX],tot;
void add_edge(int u,int v){
	E[tot].v=v;E[tot].nxt=head[u];head[u]=tot++;
}
int fa[MX],T[MX];
ll a[MX],b[MX];
bool del[MX];
int Find(int x){
	return del[fa[x]]?(fa[x]=Find(fa[x])):fa[x];
}
void dfs(int u,int f){
	fa[u]=f;
	for(int i=head[u];~i;i=E[i].nxt){
		int v=E[i].v;
		if(v==f) continue;
		dfs(v,u);
	}
}
void init(int _n){
	for(int i=1;i<=_n;i++){
		T[i]=del[i]=0;
		head[i]=-1;
	}
	tot=0;
	a[1]=0;b[1]=0;
}

int main(){
	//FIN;
	for(scanf("%d",&_);_;_--){
		scanf("%d",&n);
		init(n);
		priority_queue<node>q;
		for(int i=2;i<=n;i++){
			scanf("%lld%lld",&a[i],&b[i]);
			q.push(node{a[i],b[i],0,i});
		}
		for(int i=1;i<n;i++){
			int u,v;scanf("%d%d",&u,&v);
			add_edge(u,v);add_edge(v,u);
		}
		dfs(1,0);
		int cnt=0;
		while(!q.empty()){
			node nw=q.top();q.pop();
			int id=nw.id,t=nw.t;
			if(del[id]) continue;
			if(T[id]!=t) continue;
			int f=Find(id);del[id]=1;
			ll tmp1=max(a[f],a[f]+a[id]-b[f]);
			ll tmp2=b[f]+b[id]-a[f]-a[id]+tmp1;
			a[f]=tmp1;b[f]=tmp2;
			if(f>1){
				node nxt={a[f],b[f],(T[f]=++cnt),f};
				q.push(nxt);
			}
		}
		printf("%lld\n",a[1]);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值