hdu6326 2018杭电多校3H Monster Hunter

题目:

给定一棵 n n n个结点的树,除了结点1外的每一个结点上有一个怪物,到达一个结点后一定要打这个结点上的怪物,打第 i i i个结点上的怪物后会扣 a i a_i ai滴血,打完后会加 b i b_i bi滴血,每个结点上的怪物打过后再到达这个结点就不用打怪了,从结点1出发,血量小于0时游戏结束,问最少的初始血量,可以打完所有的怪。
( 2 ≤ n ≤ 1 0 5 , 0 ≤ a i , b i ≤ 1 0 9 ) (2 \le n \le 10^5,0 \le a_i,b_i \le 10^9) (2n105,0ai,bi109)

题解:

可以发现一个结点的怪只有在将其父结点上的怪打掉后才有机会打,将无根树转化为以1为根的有根树,那么我们就是要在满足树的拓扑序的限制下安排打怪的最优顺序。
先考虑没有树的拓扑序限制的情况,那么可以将怪分为两种情况,一种 a i < b i a_i<b_i ai<bi,一种 a i > b i a_i>b_i ai>bi,显然第一种打完是回血的,第二种打完是扣血的,那么肯定第一种排在第二种前面。对于第一种内部的顺序,考虑两个相邻的结点 i , j i,j i,j,在这之前还剩的血量为 v a l val val,那么如果 i i i排在 j j j前面,要满足 min ⁡ { v a l − a i , v a l − a i + b i − a j } < min ⁡ { v a l − a j , v a l − a j + b j − a i } \min\{val-a_i,val-a_i+b_i-a_j\}<\min\{val-a_j,val-a_j+b_j-a_i\} min{valai,valai+biaj}<min{valaj,valaj+bjai},将 v a l val val提出来并消掉,式子变成 min ⁡ { − a i , − a i + b i − a j } > min ⁡ { − a j , − a j + b j − a i } \min\{-a_i,-a_i+b_i-a_j\}>\min\{-a_j,-a_j+b_j-a_i\} min{ai,ai+biaj}>min{aj,aj+bjai},由于 a i < b i , a j < b j a_i<b_i,a_j<b_j ai<bi,aj<bj,所以 − a i + b i − a j > − a j , − a j + b j − a i > − a i -a_i+b_i-a_j>-a_j,-a_j+b_j-a_i>-a_i ai+biaj>aj,aj+bjai>ai,那么如果 − a i > − a j -a_i>-a_j ai>aj,即 a i < a j a_i<a_j ai<aj,那么左式大于右式,如果 a i > a j a_i>a_j ai>aj,即 − a i < − a j -a_i<-a_j ai<aj,那么右式大于左式,所以左式大于右式等价于 a i < a j a_i<a_j ai<aj。同理,第二种内部的先后顺序,如果 i i i排在 j j j前面,那么要有 b i > b j b_i>b_j bi>bj
现在考虑加进树的拓扑序限制,如果按照前面的规则排序,可以得到序列 p 1... n − 1 p_{1...n-1} p1...n1,其中结点1已经不算在内了,如果 p 1 p_1 p1的父结点为 1 1 1,那么 p 1 p_1 p1就可以确定为下一个点,不然,我们可以知道如果 f a p 1 fa_{p_1} fap1被确定了,那么下一个肯定是 p 1 p_1 p1,所以我们可以把 p 1 p_1 p1合并到 f a p 1 fa_{p_1} fap1,具体的 f a p 1 fa_{p_1} fap1的属性 a a a变成合并后的最小值,属性 b b b变成最小值后面加上去的值,因为这样合并的信息依然满足之前分析的排序规则,这样合并后相当于 f a p 1 fa_{p_1} fap1被确定后, p 1 p1 p1也就同时被确定了,然后将 p 1 p_1 p1的所有子结点的父结点变成 f a p 1 fa_{p_1} fap1。合并完以后问题规模就从 n − 1 n-1 n1变成了 n − 2 n-2 n2,每次都这样处理即可得到答案,其中维护序列可以用 s e t set set,合并可以用并查集。其中在写 s e t set set c m p cmp cmp的时候一定不要出现大于小于都返回 f a l s e false false的情况,因为这样 s e t set set会认为这两个值是一样的,会同时被删掉,可以用一个唯一标识 i d id id来解决这个问题。

复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=2e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int T,n,cnt,rnd;
vector<int>g[maxn];
int fa[maxn],del[maxn],pre[maxn];
ll cur,mn;
struct node{
	ll a,b;
	int ii;
	node(){}
	node(ll a,ll b):a(a),b(b){}
	bool operator<(node &ot)const{
		int id=a<b,otid=ot.a<ot.b;
		if(id==otid){
			if(id==1){
				if(a==ot.a){
					return ii<ot.ii;
				}
				return a<ot.a;
			}
			else{
				if(b==ot.b){
					return ii<ot.ii;
				}
				return b>ot.b;
			}
		}
		return id>otid;
	}
}buf[maxn];
struct cmp{
	bool operator()(int a,int b){
		return buf[a]<buf[b];
	}
};
set<int,cmp>se;
void dfs(int u,int f){
	fa[u]=f;
	for(auto v:g[u]){
		if(v==f)continue;
		dfs(v,u);
	}
}
void init(){
	for(int i=1;i<=n;i++){
		pre[i]=i;
	}
	cnt=0;
}
int find(int u){
	if(pre[u]==u)return u;
	return pre[u]=find(pre[u]);
}
void merge(){
	int u=*se.begin();
	se.erase(se.begin());
	int v=find(fa[u]);
	if(del[v]){
		del[u]=1;
		cur-=buf[u].a;
		mn=min(mn,cur);
		cur+=buf[u].b;
	}
	else{
		se.erase(v);
		ll aa,bb;
		aa=min(-buf[v].a,-buf[v].a+buf[v].b-buf[u].a);
		bb=-buf[v].a+buf[v].b-buf[u].a+buf[u].b-aa;
		buf[v].a=-aa;buf[v].b=bb;
		se.insert(v);
		pre[u]=v;
	}
}
int main(void){
	// freopen("in.txt","r",stdin);
	scanf("%d",&T);
	rnd=0;
	while(T--){
		++rnd;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)g[i].clear();
		int u,v;
		for(int i=2;i<=n;i++){
			scanf("%lld%lld",&buf[i].a,&buf[i].b);
			buf[i].ii=i;
		}
		for(int i=1;i<=n-1;i++){
			scanf("%d%d",&u,&v);
			g[u].pb(v);
			g[v].pb(u);
		}
		se.clear();
		init();
		dfs(1,0);
		for(int i=2;i<=n;i++){
			del[i]=0;
			se.insert(i);
		}
		buf[1].a=buf[1].b=0;
		del[1]=1;
		cur=0;
		mn=1e18;
		while(!se.empty()){
			merge();
		}
		printf("%lld\n",-mn);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值