• [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

    这个做法的问题在于,最优化的是一个局部答案,每次都选择的是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最大就可以了

    代码:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. struct node{
    6. int a,b;
    7. bool operator < (const node &t)const{
    8. return a
    9. }
    10. }arr[500005];
    11. long long ans=1;
    12. const int mod=1000000007;
    13. bool cmp(const node &x,const node &y)
    14. {
    15. return 1ll*(ans+x.b)*y.a>1ll*(ans+y.b)*x.a;
    16. }
    17. int main()
    18. {
    19. int n,i;
    20. scanf("%d",&n);
    21. for(i=1;i<=n;i++)
    22. scanf("%d",&arr[i].a);
    23. for(i=1;i<=n;i++)
    24. scanf("%d",&arr[i].b);
    25. sort(arr+1,arr+n+1);
    26. for(i=1;i<=n;i++)
    27. if(arr[i].a==1)
    28. ans=ans+1ll*arr[i].b;
    29. else
    30. break;
    31. if(i<=n)
    32. sort(arr+i,arr+n+1,cmp);
    33. if(i<=n && 1ll*ans*arr[i].a1ll*arr[i].b){
    34. ans=ans+1ll*arr[i].b;
    35. i++;
    36. }
    37. ans%=mod;
    38. for(;i<=n;i++)
    39. ans=1ll*ans*arr[i].a%mod;
    40. printf("%lld",ans);
    41. }

  • 相关阅读:
    炒股使用杠杆的原理是为什么?
    7_1数据结构与算法基础:::数据结构
    音频信号分析与实践
    ubuntu22.04 CH340/CH34x 驱动安装
    八、单臂路由实验
    一文了解 Java 中的构造器
    STL:map/multimap容器详解
    使用Docker-Java监听Docker容器的信息
    Java中的参数传递到底是值传递还是参数传递
    UG\NX二次开发 取消抑制特征 UF_MODL_unsuppress_feature
  • 原文地址:https://blog.csdn.net/C20180602_csq/article/details/126019057