http://poj.org/problem?id=2796

https://nanti.jisuanke.com/t/38228

背景

给定一个序列,对于任意区间,min表示区间中最小的数,sum表示区间和,求使得min*sum最大的区间或区间值。

POJ-2796中,序列的值非负,而在网络赛I题中,序列存在负值。

解法分析

直觉上,后者是前者的拓展,我们先考虑序列非负的情况。

非负情况

设序列存储在数组a中。当我们考虑数值a[i]作为区间最小值时,显然我们应该向i的左右两侧扩展并终止于遇到更小的值或者数组越界之前,这样得到的区间可以保证a[i]为最小值且区间和最大。

但是每次都如此求解,复杂度为$O\left ( n^2 \right )$。因此我们尝试用单调栈将其降低到$O\left ( n \right )$。

从我们刚刚的分析中可以看出,最小值是求解区间的关键,同时由于我们是顺序对数组进行处理(以从左往右为例),我们可以利用单调栈,在当遇到a[i]时,很快地找到左侧最近的比它更小的数值(具体而言,构建一个约束为单调递增的栈,当遇到a[i]时,逐个pop掉比它大的数),那么我们可以进一步拓展,借助单调栈维护以a[i]为右端点且为最小值的区间和,我们将栈中节点表示为Node{min,sum}(原单调栈中只存储数值,现在我们要额外存储这个区间的区间和),也就是上文中i向左侧扩展的区间情况,我们将被pop的node.sum求和再加上a[i]就得到了node[i]的左侧区间和了。但是还有i右侧的情况呢,我们先把node[i]压入栈继续处理之后的数字,注意到在随后的处理中当且仅当遇到了第一个比a[i]要小的数字时,node[i]会被pop出来,那么此轮pop中比node[i]先pop出来的若干Node合并在一起显然就是i右侧的区间了,两者进一步合并就得到了以a[i]为最小值,min*sum最大的区间了。需要额外注意的是,一个Node可能右侧没有比它更小的数值,那么在算法最后,需要将栈逐个pop出来做如上操作。

此外POJ-2796还需要计算区间端点位置(如有多个可选区间,任意输出),那么在Node中额外记录一下即可,不做过多说明。

由于每一个元素最多入栈一次,一旦被pop就不会再被查询到,显然复杂度为$O\left ( n \right )$。

代码的main函数,为了迁移到下文的题目中,做了额外修改,本节可以忽略。

  1. #include<cstdio>
  2. #include<cstdlib>
  3. #include<cstring>
  4. #include<string>
  5. #include<algorithm>
  6. #include<iostream>
  7. #include<queue>
  8. #include<map>
  9. #include<cmath>
  10. #include<set>
  11. #include<stack>
  12. #define LL long long
  13. using namespace std;
  14. const int N = ;
  15. LL n, m;
  16. LL a[N];
  17. struct node
  18. {
  19. LL minv;
  20. LL sum;
  21. LL lef;
  22. node(LL mv,LL s,LL l)
  23. {
  24. minv=mv,sum=s,lef=l;
  25. }
  26. };
  27. //10 4 8 3 2 6 8 4 9 3 7
  28. int al,ar;
  29. LL glo_ans;
  30. void cal(int l,int r)
  31. {
  32. stack<node> s;
  33. LL pop_sum,pop_lef;
  34. for(int i=l; i<=r; i++)
  35. {
  36. pop_sum=;
  37. pop_lef=i;
  38. while(!s.empty()&&a[i]<=s.top().minv)
  39. {
  40. node p=s.top();
  41. s.pop();
  42. pop_lef=p.lef;
  43. pop_sum+=p.sum;
  44. if(pop_sum*p.minv>glo_ans)
  45. glo_ans=pop_sum*p.minv,al=p.lef,ar=i-;
  46. //ans=max(ans,pop_sum*p.minv);
  47. }
  48. s.push(node(a[i],a[i]+pop_sum,pop_lef));
  49. }
  50. pop_sum=;
  51. while(!s.empty())
  52. {
  53. node p=s.top();
  54. s.pop();
  55. pop_sum+=p.sum;
  56. if(pop_sum*p.minv>glo_ans)
  57. glo_ans=pop_sum*p.minv,al=p.lef,ar=r;
  58. }
  59. }
  60. int main()
  61. {
  62. //freopen("in.txt","r",stdin);
  63. //freopen("out.txt","w",stdout);
  64. //cin.sync_with_stdio(false);
  65. int n;
  66. while(scanf("%d",&n)!=EOF)
  67. {
  68. glo_ans=,al=ar=;
  69. for(int i=; i<=n; i++)
  70. scanf("%lld",&a[i]);
  71.  
  72. int pl=-,pr=;
  73. for(int i=; i<=n; i++)
  74. {
  75. if(a[i]>)
  76. {
  77. if(pl==-)
  78. pl=i;
  79. pr=i;
  80. if(i==n||a[i+]<=)
  81. {
  82. cal(pl,pr);
  83. }
  84. }
  85. else
  86. pl=-;
  87. }
  88.  
  89. //cal(1,n);
  90. LL tx=,mx=a[al];
  91. for(int i=al;i<=ar;i++)
  92. tx+=a[i],mx=min(mx,a[i]);
  93. printf("%lld\n",glo_ans);
  94. //cout<<glo_ans<<endl;
  95. printf("%d %d\n",al,ar);
  96. //cout<<al<<' '<<ar<<endl;
  97. }
  98. return ;
  99. }

存在负数的情况

存在负数的序列则不能简单地认为可以向左右随意扩张,因为sum不一定随着区间扩张而增长。但经过分析,我们可以发现,无论序列中的数值是什么,最大的min*sum一定是一个非负数,那么只可能有正数乘以正数或负数乘以负数的情况(答案为零的情况无需额外求解,初始值设置为0即可)。

前者,我们可以将序列分成若干非负子区间,用上文的方法求解。

后者,对于任意负数,随着区间的扩张,最小值只会减小不会增大,这样我们只要枚举每一个负数并假设a[i]为最小值,找到区间和最小的区间,求出min*sum,在枚举中保留最大答案即可(与正数的情况不同,我们扩张数组不必担心会使最小值变大,只用考虑区间和的问题),不必担心找到的区间具有更小的最小值,因为我们枚举了每一个负数,那么更小的最小值必然也会被枚举,不会漏掉答案。那么如何找到区间和最小的区间呢?我们可以计算数组的前缀和以及后缀和,i左侧后缀和最小以及i右侧前缀和最小的位置构成的区间既是区间和最小区间,为了快速索引,可以再引入两个数组用于记录i左侧后缀/右侧前缀和最小的位置。

  1. #include <iostream>
  2. #include <vector>
  3. #include <stack>
  4. #define LL long long
  5. using namespace std;
  6. const int N=;
  7. LL inf=;
  8. LL pre[N],las[N];
  9. LL idp[N],idl[N];
  10. LL a[N];
  11. int n;
  12. struct node
  13. {
  14. LL minv;
  15. LL sum;
  16. node(LL mv,LL s)
  17. {
  18. minv=mv,sum=s;
  19. }
  20. };
  21.  
  22. LL glo_ans;
  23. void cal(int l,int r)
  24. {
  25. //cout<<l<<' '<<r<<endl;
  26. stack<node> s;
  27. LL pop_sum;
  28. for(int i=l; i<=r; i++)
  29. {
  30. pop_sum=;
  31. while(!s.empty()&&a[i]<=s.top().minv)
  32. {
  33. node p=s.top();
  34. s.pop();
  35. pop_sum+=p.sum;
  36. if(pop_sum*p.minv>glo_ans)
  37. glo_ans=pop_sum*p.minv;
  38. //ans=max(ans,pop_sum*p.minv);
  39. }
  40. s.push(node(a[i],a[i]+pop_sum));
  41. }
  42. pop_sum=;
  43. while(!s.empty())
  44. {
  45. node p=s.top();
  46. s.pop();
  47. pop_sum+=p.sum;
  48. if(pop_sum*p.minv>glo_ans)
  49. glo_ans=pop_sum*p.minv;
  50. }
  51. }
  52.  
  53. int main()
  54. {
  55. while(scanf("%d",&n)!=EOF)
  56. {
  57. LL ans = ;
  58. glo_ans=-inf;
  59. for(int i=; i<=n; i++)
  60. {
  61. scanf("%lld",&a[i]);
  62. idp[i]=idl[i]=i;
  63. glo_ans=max(glo_ans,a[i]);
  64. }
  65. int pl=-,pr=;
  66. for(int i=; i<=n; i++)
  67. {
  68. if(a[i]>)
  69. {
  70. if(pl==-)
  71. pl=i;
  72. pr=i;
  73. if(i==n||a[i+]<=)
  74. {
  75. cal(pl,pr);
  76. }
  77. }
  78. else
  79. pl=-;
  80. }
  81. fill(las,las+n+,);
  82. fill(pre,pre+n+,);
  83. for(int i=; i<=n; i++)
  84. pre[i]=pre[i-]+a[i];
  85. for(int i=n; i>=; i--)
  86. las[i]=las[i+]+a[i];
  87. for(int i=; i<=n; i++)
  88. if(las[i]>las[idl[i-]])
  89. idl[i]=idl[i-];
  90. for(int i=n-; i>=; i--)
  91. if(pre[i]>pre[idp[i+]])
  92. idp[i]=idp[i+];
  93. for(int i=; i<=n; i++)
  94. {
  95. if(a[i]<)
  96. {
  97. LL lef=las[idl[i]]-las[i+];
  98. LL rig=pre[idp[i]]-pre[i-];
  99.  
  100. LL sm=lef+rig-a[i];
  101.  
  102. ans=max(ans,sm*a[i]);
  103. }
  104. }
  105. printf("%lld\n",max(ans,glo_ans));
  106. }
  107. return ;
  108. }

POJ-2796 & 2019南昌邀请赛网络赛 I. 区间最大min*sum的更多相关文章

  1. 2019南昌邀请赛网络赛:J distance on the tree

    1000ms 262144K   DSM(Data Structure Master) once learned about tree when he was preparing for NOIP(N ...

  2. [2019南昌邀请赛网络赛D][dp]

    https://nanti.jisuanke.com/t/38223 Xiao Ming recently indulges in match stick game and he thinks he ...

  3. 2019南昌邀请赛网络预选赛 M. Subsequence

    传送门 题意: 给出一个只包含小写字母的串 s 和n 个串t,判断t[i]是否为串 s 的子序列: 如果是,输出"YES",反之,输出"NO": 坑点: 二分一 ...

  4. 南昌邀请赛网络赛 D.Match Stick Game(dp)

    南昌邀请赛网络赛 D.Match Stick Game 题目传送门 题目就会给你一个长度为n的字符串,其中\(1<n<100\).这个字符串是一个表达式,只有加减运算符,然后输入的每一个字 ...

  5. 2019 ICPC南昌邀请赛网络赛比赛过程及题解

    解题过程 中午吃饭比较晚,到机房lfw开始发各队的账号密码,byf开始读D题,shl电脑卡的要死,启动中...然后听到谁说A题过了好多,然后shl让blf读A题,A题blf一下就A了.然后lfw读完M ...

  6. 计蒜客 2019南昌邀请网络赛J Distance on the tree(主席树)题解

    题意:给出一棵树,给出每条边的权值,现在给出m个询问,要你每次输出u~v的最短路径中,边权 <= k 的边有几条 思路:当时网络赛的时候没学过主席树,现在补上.先树上建主席树,然后把边权交给子节 ...

  7. 2019 ICPC南昌邀请赛 网络赛 K. MORE XOR

    说明 \(\oplus x​\)为累异或 $ x^{\oplus(a)}​$为异或幂 题意&解法 题库链接 $ f(l,r)=\oplus_{i=l}^{r} a[i]$ $ g(l,r)=\ ...

  8. icpc 南昌邀请赛网络赛 Max answer

    就是求区间和与区间最小值的积的最大值 但是a[i]可能是负的 这就很坑 赛后看了好多dalao的博客 终于a了 这个问题我感觉可以分为两个步骤 第一步是对于每个元素 以它为最小值的最大区间是什么 第二 ...

  9. icpc 南昌邀请赛网络赛 Subsequence

    题目链接:https://nanti.jisuanke.com/t/38232 就是判断输入是不是子序列 没想到贡献了将近十几次罚时..........可以说是菜的真实了 用cin cout超时了 改 ...

随机推荐

  1. #WEB安全基础 : HTTP协议 | 0x10 扩展HTTP报文结构概念和内容编码

    #以后的知识都是HTTP协议的扩展,如果精力有限可以选择暂时忽略,注意只是暂时忽略,以后的东西同样重要 HTTP传输数据时可以直接传输也可以对数据进行编码,由于编码在计算机内运行,所以会占用一些CPU ...

  2. 从拥抱开源到回馈开源,灵雀云助力CNCF中国区培训业务

    6月27日,全球首屈一指的开源盛会 2018 LinuxCon + ContainerCon + CloudOpen China (LC3)在中国北京国家会议中心落下帷幕.二度落地中国的LC3大会热度 ...

  3. textbox 未

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Tex ...

  4. PHP提交失败保留填写后的信息

    index.html: <html> <head> <title>jQuery Ajax 实例演示</title> </head> < ...

  5. zw字王《中华大字库》2018版升级项目正式启动

    zw字王<中华大字库>2018版升级项目正式启动 https://www.cnblogs.com/ziwang/p/9500537.html 这次升级是和字库协会一起合作,首批推出的字体, ...

  6. Qt3D Shader

    --------------------------------------------------- Qt3D ShaderPrograme Qt3D GLSL 渲染器 Shader示例可参考: h ...

  7. python的对象 变量

    对象 所有的数据类型都是对象 字符串 数字:整数.数字 列表.字典 函数.类.模块.方法 print(type(20)) # 查看对象的数据类型 python整型: int  浮点型 :float 精 ...

  8. Jupyter Notebooks 是数据科学/机器学习社区内一款非常流行的工具

    Jupyter Notebooks 是数据科学/机器学习社区内一款非常流行的工具.Jupyter Notebooks 允许数据科学家创建和共享他们的文档,从代码到全面的报告都可以.李笑来 相当于拿他来 ...

  9. NI_NUMERICHOST" is not exported by the Socket module "getaddrinfo" is not expo

    [root@Server3 ~]# masterha_check_repl --conf=/etc/masterha/app1.cnf "NI_NUMERICHOST" is no ...

  10. 基于ROS的人脸识别

    #!/usr/bin/env python # -*- coding: utf-8 -*- import rospy import cv2 import numpy as np from sensor ...