题意:
- 有 m 场战斗,每场战斗前在 n 种单位中选择一种,使得用拥有的钱招募士兵能打败怪物,同时尽可能少地花费金钱。
- 判定打败怪物的公式是:
∑
(
h
i
)
/
D
i
>
H
i
/
∑
(
d
i
)
∑(hi)/Di > Hi/∑(di)
∑(hi)/Di>Hi/∑(di),即士兵攻击的秒伤 严格大于 怪物攻击的秒伤,士兵死完前必定先杀死怪物。这个公式显然可以移一下项 变成
∑
(
h
i
?
d
i
)
>
H
i
?
D
i
∑(hi*di) > Hi*Di
∑(hi?di)>Hi?Di,右边这个东西对于每个怪物是固定的我们定义成 HD。
推完公式后就可以开摆了 ,我们从一个全新的角度去维护,定义 cost[i] 为花费 i 金钱能获取的最大攻击力(也就是
∑
(
h
i
?
d
i
)
∑(hi*di)
∑(hi?di)),如果某个 cost[i] 严格大于了 HD,就代表 i 花费能战胜这个怪物,是一个可行解。- 花费的范围达到了 1e6,该怎么维护?答案用数学中的调和级数维护我们的 cost :
forr(i, 1, n) {
ll a, b, d;
cin >> a >> b >> d;
c[a] = max(c[a], b * d);
}
for (int i = 1; i <= m; i++)
for (int j = 1; i * j <= m; j++)
cost[i * j] = max(cost[i * j], c[i] * j);
forr(i, 1, m)cost[i] = max(cost[i], cost[i - 1]);
- 可以发现我们的 cost 数组是单调递增的,所以可以结合二分优化搜索的过程。只需要搜索第一个攻击力严格大于 HD 的位置即可,此位置的花费必定最小,如果找不到代表杀不死。
C
o
d
e
:
Code:
Code:
#include<bits/stdc++.h>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
#define all(a) a.begin(),a.end()
#define oper(a) (operator<(const a& ee)const)
#define endl "\n"
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
double DNF = 1e17;
const int N = 200010, M = 1000010, MM = 110;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
int n, m, k, T, S, D, K;
ll cost[M], c[M];
void solve() {
cin >> n >> m;
forr(i, 1, n) {
ll a, b, d;
cin >> a >> b >> d;
c[a] = max(c[a], b * d);
}
for (int i = 1; i <= m; i++)
for (int j = 1; i * j <= m; j++)
cost[i * j] = max(cost[i * j], c[i] * j);
forr(i, 1, m)cost[i] = max(cost[i], cost[i - 1]);
cin >> k;
forr(i, 1, k) {
ll a, b, mos;
cin >> a >> b;
mos = a * b;
int t = upper_bound(cost + 1, cost + 1 + m, mos) - cost;
if (t == m + 1)cout << -1;
else cout << t;
cout << ' ';
}
}
int main() {
cinios;
T = 1;
while (T--)solve();
return 0;
}
|