欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

【牛客NOIP模拟】牛半仙的魔塔(增强版)【贪心】【并查集】

程序员文章站 2022-03-16 08:16:36
原来并查集还可以vector记所有儿子...

题意:一个魔塔游戏的地图是一棵以 1 1 1 为根的树,起点为根,除根外每个结点有一个怪物,给定每个怪物血量、攻击、防御、奖励蓝宝石个数(加防御),勇士的血量、攻击、防御,遇到怪物必须战斗,勇士永远先手,求击败所有怪物后最多剩余血量。

n ≤ 1 0 5 n\leq 10^5 n105,所有战斗防御小于攻击。

由于不能加攻击,所以打败一个怪物受到的攻击次数是固定的,算出来记为 k i k_i ki,奖励宝石记为 b i b_i bi。而每获得一个宝石可以让之后的所有怪物减伤 k i k_i ki

先考虑菊花图,也就是没有顺序限制。

考虑一个解 { p 1 , p 2 , p 3 , … , p n } \{p_1,p_2,p_3,\dots,p_n\} {p1,p2,p3,,pn} ,如果交换 p i , p i + 1 p_i,p_{i+1} pi,pi+1 让解变优,即

d k p i + ( d + d p i ) k p i + 1 < d k p i + 1 + ( d + d p i + 1 ) k p i dk_{p_i}+(d+d_{p_i})k_{p_{i+1}}<dk_{p_{i+1}}+(d+d_{p_{i+1}})k_{p_i} dkpi+(d+dpi)kpi+1<dkpi+1+(d+dpi+1)kpi

d p i k p i < d p i + 1 k p i + 1 \frac{d_{p_i}}{k_{p_i}}<\frac{d_{p_{i+1}}}{k_{p_{i+1}}} kpidpi<kpi+1dpi+1

所以在没有先决条件的情况下,按 d i k i \frac{d_i}{k_i} kidi 从小到大排序一个个选就可以了。定义性价比为 c i = d i k i c_i=\frac{d_i}{k_i} ci=kidi

考虑树的情况,删除点 u u u 后,要么继续删儿子,要么删其他点废话

如果 u u u 性价比最高的儿子 v v v 性价比比 u u u 高,那么显然一定会继续删 v v v

所以我们可以把 u , v u,v u,v 合并成一个点, d 、 k d、k dk 相加重新计算性价比。这时候 u u u 的其他儿子需要与合并后的点比较。

实现的时候把所有怪物压进按性价比排序的大根堆,每次选出最大的,如果父亲性价比比它低(也就是还在堆里)就和父亲合并,否则删掉这个点所在的大点,可以用并查集实现。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
#include <vector>
#include <utility>
#define MAXN 100005
using namespace std;
typedef long long ll;
inline ll read()
{
	ll ans=0;
	char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
vector<int> e[MAXN],son[MAXN];
int bns[MAXN],cnt[MAXN],a[MAXN],b[MAXN],k[MAXN];
int fa[MAXN],rt[MAXN];
int find(int x){return rt[x]==x? x:rt[x]=find(rt[x]);}
typedef pair<double,int> pi;
priority_queue<pi> q;
ll pb,pa,pd;
int vis[MAXN],del[MAXN];
void dfs(int u,int f){fa[u]=f;for (int i=0;i<(int)e[u].size();i++) if (e[u][i]!=f) dfs(e[u][i],u);}
void dfs(int u)
{
	pb-=(a[u]-pd)*cnt[u],pd+=bns[u],del[u]=1;
	for (int i=0;i<(int)son[u].size();i++) dfs(son[u][i]);
}
int main()
{
	int n=read();
	for (int i=1;i<n;i++)
	{
		int u,v;
		u=read(),v=read();
		e[u].push_back(v),e[v].push_back(u);
	}
	pb=read(),pa=read(),pd=read();
	for (int i=2;i<=n;i++)
	{
		int bl,d;
		bl=read(),a[i]=read(),d=read(),bns[i]=b[i]=read();
		cnt[i]=k[i]=(bl-1)/(pa-d);
	}
	dfs(1,0);
	for (int i=2;i<=n;i++) q.push(make_pair(1.0*b[i]/k[i],rt[i]=i));
	del[1]=1;
	while (!q.empty())
	{
		int u=q.top().second;q.pop();
		if (vis[u]) continue;
		vis[u]=true;
		if (del[fa[u]]) dfs(u);
		else
		{
			int f=find(fa[u]);
			k[f]+=k[u],b[f]+=b[u];
			son[rt[u]=f].push_back(u);
			q.push(make_pair(1.0*b[f]/k[f],f));
		}
	}
	if (pb<=0) pb=-1;
	cout<<pb;
	return 0;
}

本文地址:https://blog.csdn.net/luositing/article/details/109239171