【GOJ 1489】Monster Hunter

题目

正解

首先,这是一棵有根树,其次,很明显每只怪物都要父亲怪物被击杀后才可以被击杀,我们不妨想问题的时候从简单的出发,就是:假如没有父亲这个限制,我们应该怎样打怪物呢,首先我们可以把怪物分成两类: (a lt b)(a geq b)的,前面一类打完不会掉血,后面则会掉血,那么这时候肯定先把前面一类的打完之后再打第二类的是更加优的。

  • (a lt b)的怪物呢,我们也应该按照a从小到大的顺序打。

  • (a geq b)的怪物呢,考虑两只怪物的击杀顺序(i,j),那么有两种情况:先打(i)或者先打(j)

  • (i)(HP+min(-a[i],-a[i]+b[i]-a[j])), 因为(age b) ,所以有(HP-a[i]-a[j]+b[i])

    • 先打(j)同理有(HP-a[i]-a[j]+b[j]),可以发现 (HP-a[i]-a[j])是一样需要的,所以我们应该按照b的从大到小的顺序打。

那么我们这样就可以在(O(1))的时间内比较出应该先打谁了,假设忽略了父亲限制之后的攻击顺序为(p_1,p_2,..,p_n)

  • (p_1=1),那么第一步打(p_1)一定是最优的。

  • (p_1!=1),那么在打完(p_1)的父亲(x)后,紧接着打(p_1)一定是最优的,直接把(p_1)(x)的两只怪物合并,依然用((a,b))表示,并且把(p_1)所有儿子的父亲改为(x)就可以消除(p_1)的影响了,么每次消除(1)个,算法复杂度为(o(n))

很明显需要支持修改一个怪物,删除最优怪物,以为修改父亲的操作;那么找最优怪物,我们可以用优先队列,对于修改父亲,我们可以用并查集维护就ok,所以总的复杂度应该是(o(nlogn))

代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
typedef long long ll;
char cch;
inline int rd(){
	int x=0,fl=1;
	cch=getchar();
	while(cch>'9'||cch<'0'){
		if(cch=='-') fl=-1;
		cch=getchar();
	}
	while(cch>='0'&&cch<='9') x=(x<<3)+(x<<1)+cch-'0',cch=getchar();
	return x*fl;
}
const int N=1e5+3;
struct abc{
	int i;
	ll a,b;
	bool operator <(const abc & x)const{
		int sn1=a<b,sn2=x.a<x.b;
		if(sn1<sn2) return true;
		if(sn1==sn2){
			if(a<b) return a>x.a;
			return b<x.b;
		}
		return 0;
	}
}a[N];
bool vis[N];
int to[N<<1],cnt,nxt[N<<1],fa[N],head[N];
inline void adde(int u,int v){
	to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
	to[++cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
inline void dfs(int x,int f){
	fa[x]=f;
	for(int i=head[x];i;i=nxt[i]) if(to[i]!=f){
		dfs(to[i],x);
	}
}
inline int getfa(int x){
	if(!fa[x]||!vis[x]) return x;
	return fa[x]=getfa(fa[x]);
}
priority_queue<abc> q;
int main(){//问题相当于求:Hp初始为0,可以随时购买,最少需要买多少Hp 
	int n=rd(),u,v;
	abc t;
	rep(i,2,n) a[i].a=rd(),a[i].b=rd(),a[i].i=i,q.push(a[i]);
	rep(i,2,n) u=rd(),v=rd(),adde(u,v);
	dfs(1,0);
	ll tmp;
	while(!q.empty()){
		t=q.top();q.pop();
		u=t.i;
		if(vis[u]||((t.a!=a[u].a)||(t.b!=a[u].b))) continue;
		vis[u]=1;
		v=getfa(u);
		tmp=max(a[v].a,a[u].a+a[v].a-a[v].b);//购买需要的 Hp 
		a[v].b=a[u].b+a[v].b-a[u].a-a[v].a+tmp;//总受益,加上tmp是因为 你为了Hp始终>=0购买了tmp的Hp,所以相当于凭空获得tmp 
		a[v].a=tmp;
		/*if(v>1)*/ q.push(a[v]);
	}
	printf("%lld",a[1].a); 
}

来源

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

限于本人水平,如果文章有表述不当之处,还请不吝赐教。

原文地址:https://www.cnblogs.com/Sam2007/p/13121724.html