AT5760 Manga Market(dp + 二分 + 贪心)

Description

n n n 个商店,你在家里,初始时间为零。你从家走到一个商店或从一个商店走到另一个商店都需要一个单位时间。如果你在 t t t 个单位时间到达第 i i i 家商店,那么你排队 a i × t + b i a_i \times t + b_i ai×t+bi 个单位时间买东西,一家商店只能买一次。所有的商店会在 T + 0.5 T + 0.5 T+0.5 个单位时间关门,这时如果你在排队那么不算你买了东西。求你最多买多少次。

1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ a i , b i , T ≤ 1 0 9 1 \leq n \leq 2 \times 10^5, 0 \leq a_i, b_i, T \leq 10^9 1n2×105,0ai,bi,T109

Solution

可能直觉上排一个序完事了,但题目难度告诉我们这是错的,因为交换两个商店的顺序对其它的商店也会影响。

贪心不行,考虑可以 dp。令 f i , j f_{i,j} fi,j 走到的第 i i i 个商店为 j j j 的最小时间。不能走到一个商店两次,看上去没法转移。所以还是要排序。考虑在 t t t 的时间要出发,接下来要访问两个商店 i i i j j j。如果访问第一个商店比第二个商店更优,那么

1 + a i ( t + 1 ) + b i + 1 + a j ( t + 1 + a i ( t + 1 ) + b i + 1 ) + b j < 1 + a j ( t + 1 ) + b j + 1 + a i ( t + 1 + a j ( t + 1 ) + b j + 1 ) + b i ⇕ a j ( b i + 1 ) < a i ( b j + 1 ) 1 + a_i(t+1) + b_i + 1 + a_j (t + 1 + a_i(t+1) + b_i + 1) + b_j < 1 + a_j(t+1) + b_j + 1 + a_i (t + 1 + a_j(t+1) + b_j + 1) + b_i \\ \Updownarrow \\ a_j(b_i+1) < a_i(b_j+1) 1+ai(t+1)+bi+1+aj(t+1+ai(t+1)+bi+1)+bj<1+aj(t+1)+bj+1+ai(t+1+aj(t+1)+bj+1)+biaj(bi+1)<ai(bj+1)

那么可以转移了,而且状态中的 j j j 没必要可以不写。那么这是一个 O ( n 2 ) O(n^2) O(n2) 的超时 dp。可以发现 a i × t + b i a_i \times t + b_i ai×t+bi 即使 a i = 1 a_i = 1 ai=1 也是成倍增长的,这意味着如果 a i > 0 a_i > 0 ai>0 那么最多经过 l o g ( T ) log(T) log(T) 个商店,所以 30 30 30 个商店完全够了。

所以将 a i = 0 a_i = 0 ai=0 的挑出来,如果答案中经过它们,那么在最后走一定最优。可以将它们按 b i b_i bi 从小到大排序。枚举去多少个 a i > 0 a_i > 0 ai>0 的商店,二分找到可以去的 a i = 0 a_i = 0 ai=0 的商店,两部分加起来更新答案即可。

时间复杂度 O ( n log ⁡ n + n log ⁡ T ) O(n \log n + n \log T) O(nlogn+nlogT)。不要用 memset 因为你的正无穷乘上一个数可能爆 long long。

Code

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define int long long
#define F first
#define S second
typedef pair<int, int> P;
const int N = 30 + 5, INF = 0x3f3f3f3f;
inline int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
bool cmp(P a, P b) {
	return b.F * (a.S + 1) < a.F *(b.S + 1) ;
}
vector <P> a; vector <int> b;
int f[N];
signed main() {
	int n = read(), T = read();
	for (int i = 1; i <= n; i++) {
		int x = read(), y = read();
		if (x) a.pb(make_pair(x, y));
		else b.pb(y + 1);
	}
	sort(a.begin(), a.end(), cmp);
	sort(b.begin(), b.end());
	for (int i = 1; i < b.size(); i++) b[i] += b[i - 1];
	for (int i = 1; i <= 30; i++) f[i] = T + 1;
	for (int i = 0; i < a.size(); i++)
		for (int j = 30; j; j--)
			f[j] = min(f[j], (f[j - 1] + 1) * (a[i].F + 1) + a[i].S);
	int ans = 0;
	for (int i = 0; i <= 30; i++) 
		if (f[i] <= T) ans = max(ans, i + (b.empty() ? 0 : upper_bound(b.begin(), b.end(), T - f[i]) - b.begin()));
	printf("%lld\n", ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
校园失物招领系统管理系统按照操作主体分为管理员和用户。管理员的功能包括字典管理、论坛管理、公告信息管理、失物招领管理、失物认领管理、寻物启示管理、寻物认领管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 校园失物招领系统管理系统可以提高校园失物招领系统信息管理问题的解决效率,优化校园失物招领系统信息处理流程,保证校园失物招领系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 ,管理员权限操作的功能包括管理公告,管理校园失物招领系统信息,包括失物招领管理,培训管理,寻物启事管理,薪资管理等,可以管理公告。 失物招领管理界面,管理员在失物招领管理界面中可以对界面中显示,可以对失物招领信息的失物招领状态进行查看,可以添加新的失物招领信息等。寻物启事管理界面,管理员在寻物启事管理界面中查看寻物启事种类信息,寻物启事描述信息,新增寻物启事信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值