老了,老了,水题都能卡一下午
题目大意
初始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);
}