[LNOI2022] 吃(数学+贪心)

老了,老了,水题都能卡一下午

题目大意

初始x=1,n个二元组(ai,bi),可以按照任意顺序对x依次使用这n个二元组,要么将x变为x*ai,要么将x变为x+bi,求最后x可得的最大值

1<=ai,bi<=10^6   ,   n<=5*10^5

n,ai,bi均为整数

题解

首先,加法操作肯定在乘法操作之前,否则加数就吃不到前面乘法的加成

显然,当ai=1时,直接选择x  ->  x+bi操作

假设把所有ai=1的二元组都使用x  ->  x+bi,所有ai>1的二元组都使用x -> x*ai

记S=1+Σbi  [ai==1],P=Πai  [ai>1]

则最后的答案就是S*P

我们可以选择把P中的一个ai移动到S中的Σbi

贪心一下,当(S+bk)*(P/ak) > S*P时,我们就可以做这样的决策

也就是当(S+bk)/ak > S时,我们可以选择将(ak,bk)这个二元组加入到加法操作中

接下来就是一个寻找极值的问题

选择一个二元组集合T={(ap1,bp1),(ap2,bp2),(ap3,bp3)……}(可以为空)

使得(S+bp1+bp2+bp3+……)/ (ap1*ap2*ap3*……)最大

这其实是一个很不好解决的问题,许多贪心都会偏向局部最优解

比如按照bi/(ai-1)从大到小排序,如果加法比乘法的结果大就选择加法,否则后面都会更劣,就break拉倒(比如我)(其实这里的bi/(ai-1)是有道理的,S<bi/(ai-1)等价ai*S<S+bi)

这个做法的问题在于,最优化的是一个局部答案,每次都选择的是S+bi和S*ai中的最大值

但是这个选择未必能让 全局答案P*(S+Σbi)/Πai 最优

很离谱的是,这个错误的做法可以拿90分

所以还得另谋出路

看了题解才知道,实际上|T|<=1

证明也很简单

若选择了两个二元组(不妨设其为(a1,b1),(a2,b2)  , 不妨设b1<=b2)

那么答案为(S+b1+b2)/(a1*a2)

而只选其中(a2,b2)的答案为(S+b2)/a2

 (S+b1+b2)/(a1*a2)  _  (S+b2)/a2 

S+b1+b2  _   (S+b2)*a1

我们突然意识到a1和a2都是大于1的整数

S+b1+b2  _   2S+2*b2 <= (S+b2)*a1

这里结果就很显然了,因为前面不妨设了b1<=b2,所以

S+b1+b2  <   2S+2*b2 <= (S+b2)*a1

所以|T|<=1得证

所以我们只需要直接枚举需要选择的二元组,让(S+bi)/ai最大就可以了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
	int a,b;
	bool operator < (const node &t)const{
		return a<t.a;
	}
}arr[500005];
long long ans=1;
const int mod=1000000007;
bool cmp(const node &x,const node &y)
{
	return 1ll*(ans+x.b)*y.a>1ll*(ans+y.b)*x.a;
}
int main()
{
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d",&arr[i].a);
	for(i=1;i<=n;i++)
		scanf("%d",&arr[i].b);
	sort(arr+1,arr+n+1);
	for(i=1;i<=n;i++)
		if(arr[i].a==1)
			ans=ans+1ll*arr[i].b;
		else
			break;
	if(i<=n)
		sort(arr+i,arr+n+1,cmp);
	if(i<=n && 1ll*ans*arr[i].a<ans+1ll*arr[i].b){
		ans=ans+1ll*arr[i].b;
		i++;
	}
	ans%=mod;
	for(;i<=n;i++)
		ans=1ll*ans*arr[i].a%mod;
	printf("%lld",ans);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值