【香蕉oi】技能树【hdu6326】Monster Hunter(结论,贪心)

文章目录

题意

有一棵树,必须先走父亲再走儿子,但是不一定要走完一棵子树再走另一棵子树。每个点有两个权值 a i , b i a_i,b_i ai,bi

现在你可以拿一个初始权值 T T T,按上述方法遍历这棵树,在经过一个节点的时候先减去 a i a_i ai,然后加上 b i b_i bi,要求任意时刻权值大于等于0。

求最小的初始权值。

思路

先考虑没有父亲限制,每个点随时都可以取的情况。

在这道题里节点可以按照 a i a_i ai b i b_i bi的一些关系确定谁先取会更优。

首先显然 a i ≤ b i a_i\le b_i aibi的肯定优于 a i > b i a_i>b_i ai>bi的。

考虑两个 a i ≤ b i , a j ≤ b j , a i < a j a_i\le b_i,a_j\le b_j,a_i<a_j aibi,ajbj,ai<aj,显然 a a a更小的 i i i先取更优,因为 a a a更小的门槛较低且利于后面的选择。

然后考虑两个 a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj,我们应该比较 v i = max ⁡ ( a i , a i + a j − b i ) v_i=\max(a_i,a_i+a_j-b_i) vi=max(ai,ai+ajbi) v j = max ⁡ ( a j , a i + a j − b j ) v_j=\max(a_j,a_i+a_j-b_j) vj=max(aj,ai+ajbj)的大小,谁的 v v v小谁适合放前面。

其实这时候做题已经OK了,但是结论好像还可以再简单一点,所以再做一些没有太大必要的工作。。。

大力分类讨论取 max ⁡ \max max的情况:

a i ≠ a i + a j − b i a_i \neq a_i+a_j-b_i ai=ai+ajbi所以等于的情况不可能存在,在下面为了避免麻烦没有讨论进去。)

  1. { a i > a i + a j − b i a j > a i + a j − b j \left\{\begin{aligned}a_i > a_i+a_j-b_i \\ a_j > a_i+a_j-b_j \end{aligned}\right. {ai>ai+ajbiaj>ai+ajbj { a j < b i a i < b j \left\{\begin{aligned}a_j < b_i \\ a_i < b_j \end{aligned}\right. {aj<biai<bj 时, { a j < b i < a i a i < b j < a j \left\{\begin{aligned}a_j < b_i < a_i \\ a_i < b_j < a_j \end{aligned}\right. {aj<bi<aiai<bj<aj 自相矛盾,不存在。
  2. { a i > a i + a j − b i a j < a i + a j − b j \left\{\begin{aligned}a_i > a_i+a_j-b_i \\ a_j < a_i+a_j-b_j \end{aligned}\right. {ai>ai+ajbiaj<ai+ajbj b j < a j < b i < a i b_j<a_j<b_i<a_i bj<aj<bi<ai 时,因为 a j > b j a_j>b_j aj>bj所以 a i < a i + a j − b j a_i<a_i+a_j-b_j ai<ai+ajbj,所以 i i i放在前面一定比 j j j更优。
  3. { a i < a i + a j − b i a j > a i + a j − b j \left\{\begin{aligned}a_i < a_i+a_j-b_i \\ a_j > a_i+a_j-b_j \end{aligned}\right. {ai<ai+ajbiaj>ai+ajbj b i < a i < b j < a j b_i<a_i<b_j<a_j bi<ai<bj<aj 时,因为 a i > b i a_i>b_i ai>bi所以 a j < a i + a j − b i a_j<a_i+a_j-b_i aj<ai+ajbi,所以 j j j放在前面一定比 i i i更优。(就是上面那种反一下)
  4. { a i > a i + a j − b i a j > a i + a j − b j \left\{\begin{aligned}a_i > a_i+a_j-b_i \\ a_j > a_i+a_j-b_j \end{aligned}\right. {ai>ai+ajbiaj>ai+ajbj { a j > b i a i > b j \left\{\begin{aligned}a_j > b_i \\ a_i > b_j \end{aligned}\right. {aj>biai>bj 时,两式相减发现谁的 b b b大谁更优。

由上可知 a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj b i > b j b_i>b_j bi>bj i i i更优的充分条件。

所以只要比较 b b b就行了。真是优美。。。

现在对于任意两个节点,我们已经可以方便的判断他们的优先级大小关系了。然后考虑父亲对儿子的限制。

假如父亲的优先级小于儿子,那么父亲取完儿子必然紧接着要取,所以把儿子并到父亲上面,用并查集维护。最后剩下的就是一棵父亲优先级大于儿子的树,那么把所有节点丢进堆里,一个一个取出来就好了。

好题。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, fa[N], faz[N];
vector<int> to[N];
struct node{
	long long a, b;
	int id;
	node(){}
	node(long long _a, long long _b, int _id){
		a = _a; b = _b; id = _id;
	}
	bool operator < (const node &u)const{
		if (a <= b && u.a <= u.b) return a > u.a;
		else if (a <= b && u.a > u.b) return false;
		else if (a > b && u.a <= u.b) return true;
		else if (a > b && u.a > u.b) return b < u.b;
	}
	bool operator != (const node &u)const{
		return a != u.a || b != u.b;
	}
}a[N], ans;
priority_queue<node> que;
bool vis[N];

bool cmp(int x, int y)
{
	return a[x] < a[y];
}

int Find(int x)
{
	if (fa[x] == x) return x;
	return fa[x] = Find(fa[x]);
}

void merge(node &x, node y)
{
	long long a, b;
	a = max(x.a, x.a+y.a-x.b);
	b = a + (-x.a+x.b-y.a+y.b);
	x = node(a, b, x.id);
}

void dfs(int u, int _faz)
{
	fa[u] = u;
	faz[u] = _faz;
	for (int i = 0, sz = to[u].size(); i < sz; ++ i)
		if (to[u][i] != _faz)
			dfs(to[u][i], u);
}

int main()
{
	int T; scanf("%d", &T);
	for (; T--; ){
	        scanf("%d", &n);
	        for (int i = 2; i <= n; ++ i){
		        scanf("%lld%lld", &a[i].a, &a[i].b);
		        a[i].id = i;
		        que.push(a[i]);
	        }
	        for (int i = 1; i <= n; ++ i)
	        	to[i].clear();
	        for (int i = 2; i <= n; ++ i){
		        int x, y;
		        scanf("%d%d", &x, &y);
		        to[x].push_back(y);
		        to[y].push_back(x);
	        }
	        memset(vis, 0, sizeof(vis));
	        dfs(1, 0); vis[1] = 1;
	        ans = node(0, 0, 0);
	        while (!que.empty()){
		        node now = que.top(); que.pop();
		        if (now != a[now.id] || vis[now.id]) continue;
		        int real_fa = Find(faz[now.id]);
		        if (!vis[real_fa]){
			        merge(a[real_fa], now);
			        fa[now.id] = real_fa;
			        que.push(a[real_fa]);
		        }
		        else{
			        vis[now.id] = 1;
			        merge(ans, now);
		        }
	        }
	        printf("%lld\n", ans.a);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值