题意:一个魔塔游戏的地图是一棵以 111 为根的树,起点为根,除根外每个结点有一个怪物,给定每个怪物血量、攻击、防御、奖励蓝宝石个数(加防御),勇士的血量、攻击、防御,遇到怪物必须战斗,勇士永远先手,求击败所有怪物后最多剩余血量。
n≤105n\leq 10^5n≤105,所有战斗防御小于攻击。
由于不能加攻击,所以打败一个怪物受到的攻击次数是固定的,算出来记为 kik_iki,奖励宝石记为 bib_ibi。而每获得一个宝石可以让之后的所有怪物减伤 kik_iki。
先考虑菊花图,也就是没有顺序限制。
考虑一个解 {p1,p2,p3,…,pn}\{p_1,p_2,p_3,\dots,p_n\}{p1,p2,p3,…,pn} ,如果交换 pi,pi+1p_i,p_{i+1}pi,pi+1 让解变优,即
dkpi+(d+dpi)kpi+1<dkpi+1+(d+dpi+1)kpidk_{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
dpikpi<dpi+1kpi+1\frac{d_{p_i}}{k_{p_i}}<\frac{d_{p_{i+1}}}{k_{p_{i+1}}}kpidpi<kpi+1dpi+1
所以在没有先决条件的情况下,按 diki\frac{d_i}{k_i}kidi 从小到大排序一个个选就可以了。定义性价比为 ci=dikic_i=\frac{d_i}{k_i}ci=kidi。
考虑树的情况,删除点 uuu 后,要么继续删儿子,要么删其他点废话
如果 uuu 性价比最高的儿子 vvv 性价比比 uuu 高,那么显然一定会继续删 vvv 。
所以我们可以把 u,vu,vu,v 合并成一个点, d、kd、kd、k 相加重新计算性价比。这时候 uuu 的其他儿子需要与合并后的点比较。
实现的时候把所有怪物压进按性价比排序的大根堆,每次选出最大的,如果父亲性价比比它低(也就是还在堆里)就和父亲合并,否则删掉这个点所在的大点,可以用并查集实现。
复杂度 O(nlogn)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;
}