CF1657D:D. For Gamers. By Gamers.(dp,调和级数,二分)

传送门

题意:

  • 有 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 (hidi)>HiDi,右边这个东西对于每个怪物是固定的我们定义成 HD。
  • 推完公式后就可以开摆了 ,我们从一个全新的角度去维护,定义 cost[i] 为花费 i 金钱能获取的最大攻击力(也就是 ∑ ( h i ∗ d i ) ∑(hi*di) (hidi),如果某个 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);
		//招募一个这种单位,对应的 c[i] 维护一个最大攻击力
	}
 	
 	//调和级数,虽然是双层但遍历的数量很少
	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;
}
/*
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值