CF1657D. For Gamers. By Gamers. (2000) (数学)(二分)(枚举倍数)
https://codeforces.com/contest/1657/problem/D
题意:
小M在玩游戏,他有n种单位和C个金币,出战一个单位要花费ci个金币,单位有每秒di的攻击和hi的血量,他一次只能花费不多于C的金币从一种单位中出战若干个来攻打怪兽。有m个怪兽,每个怪兽有每秒dj攻击,hj血量。 单位先打死怪兽才算成功。比如DH: 4,6 的单位花费0.75s打败 8 3的怪兽,怪兽也是0.75打败单位。求每个怪兽消减的最小金币花费,或者这是不可能的。
思路:
- 我们要确定斩杀每个怪物的最小金币。根据题意推得公式:Di * Hi * k > Dj * Hj 时能够斩杀 (i是英雄, j是怪物)。
- 若对于每个英雄分别枚举k来得到所有可能的方案数目仍是n方级别的。注意到C只有1e6,每种价值的方案只用保留权值最大的。
- f[i]表示花费i个金币能得到的最大权值。这样外层枚举金币数,内层枚举倍数,根据调和级数,这是nlogn级别的
- 再让f[i]变成表示花费小于等于i个金币能得到的最大权值 f[i] = max( f[i - 1], f[i] ); 这样就可以分别对每个怪兽在f上正确的二分了。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
typedef long long ll;
const int N = 3e5 + 5;
const int M = 1e6 + 5;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const int tot = 1e6 + 2;
ll f[M];
int main () {
int n, C; cin >> n >> C;
for(int i = 1; i <= n; ++ i) {
int ci; ll di, hi; cin >> ci >> di >> hi;
f[ci] = max(f[ci], di * hi);
}
for (int i = 1; i <= C; ++ i) {
for (int j = i * 2; j <= C; j += i) {
f[j] = max(f[j], j / i * f[i]);
}
}
for ( int i = 1; i <= C; ++ i ) {
f[i] = max( f[i - 1], f[i] );
}
int m; cin >> m;
while (m --) {
ll d, h; cin >> d >> h;
ll val = d * h;
if(val >= f[C]) {
cout << -1 << " "; continue;
}
cout << upper_bound(f + 1, f + 1 + C, val) - f << " " ;
}
cout << endl;
return 0;
}