2017-HDNOIP-提高组1-小鱼干

Posted 小蒟蒻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017-HDNOIP-提高组1-小鱼干相关的知识,希望对你有一定的参考价值。

小鱼干

【问题描述】

小喵喵有n个小鱼干排成一列,其中第i个小鱼干有两种属性,美味度ai和特殊度bi。

现在小喵喵要吃掉一些小鱼干,出于一些原因,小喵喵会吃掉连续的一段区间中的所有小鱼干。

如果吃掉了[l,r]一段区间,那么小喵喵会获得一些满意度。

形式化地,总满意度=

由于只有小喵喵最喜欢的小鱼干的特殊度等于1,所以bi=1的小鱼干数量不会超过400个,其他的bi=0。

现在小喵喵可以选择任意一段区间,但是有一些小鱼干的美味度是负数,吃掉所有小鱼干不一定会获得最多的满意度。所以小喵喵想知道最大能获得的总满意度是多少。

 

【输入】

第一行一个整数n,表示小鱼干的数量。

第二行n个整数,第i个数为ai,表示美味度。

第三行n个整数,第i个数为bi,表示特殊度。

【输出】

一行一个整数,表示最大的总满意度。

【输入输出样例】

fish.in

fish.out

5

4 -2 2 -3 1

0 0 1 0 0

8

【样例解释】

吃掉前三个小鱼干会获得最多的满意度。其中8=(4+(-2)+2)*(1+0+0+1)

【数据范围】

测试数据编号

数据范围

其他限制

1 - 12

1≤N≤1000

 

13 - 16

1≤N≤100000

所有bi=0

17 - 18

1≤N≤100000

bi=1的i的数量不超过20

19 - 20

1≤N≤100000

 

对于100%的数据:n≤100000,-10^9≤ai≤10^9,bi=0 or 1,bi=1的i的数量不超过400。

对于另外0%的数据:n≤100000,小喵喵会随机地给每个小鱼干的属性赋为-10^9≤ai,bi≤10^9。并且满意度变为总满意度=

【提示】

请注意本题中的数字1与字母l的区别。(你可以根据需要缩放页面比例)

其中


考试的时候看到这道题,我的内心是崩溃的。一个是因为对求和符号抱有自然的恐惧,另一个就是感觉数据量有点大。想了半天,最后还是用了前缀和+暴力,其中特殊考虑了一下全部a都为负的情况。得分60(也就是前12个点),后面的点时间超限。代码如下:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 using namespace std;
 8 long long a[101010];
 9 int b[101010];
10 int main()
11 {
12     int n;
13     scanf("%d",&n);
14     bool ok=false;
15     a[0]=0;b[0]=0;
16     for(int i=1;i<=n;i++)
17     {
18      long long x;
19      scanf("%lld",&x);
20      a[i]=a[i-1]+x;
21      //printf("%lld  ",a[i]);
22      if(x>0) ok=true;
23     }
24     long long maxn=-999999999;
25     for(int i=1;i<=n;i++)
26     {
27      int y;
28      scanf("%d",&y);
29      if(!ok) maxn=max(maxn,(a[i]-a[i-1])*(y+1));
30      b[i]=b[i-1]+y;
31     }
32     if(!ok) {printf("%lld",maxn);return 0;}
33     long long ans=-999999999;
34     for(int i=1;i<=n;i++)
35     {
36         for(int j=0;j<i;j++)
37         {
38          long long sum=(a[i]-a[j])*(b[i]-b[j]+1);
39          ans=max(ans,sum);
40         }
41     }
42     printf("%lld",ans);
43     return 0; 
44 }
A60

后来考完之后,我看了一些AC的代码,发现他们的方法都惊人的相似。
他们都是记录了b中1的位置,然后按1的位置把一长串数分为几部分,然后:①在每段内求最大连续和;②考虑一段内b都是0的是否存在最大。

大神代码如下:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cmath>
 7 using namespace std;
 8 long long read()
 9 {
10     long long x=0,f=1;char ch=getchar();
11     while(!isdigit(ch)){if(ch==\'-\') f=-1;ch=getchar();}
12     while(isdigit(ch)){x=x*10+ch-\'0\';ch=getchar();}
13     return x*f;
14 }
15 long long n;
16 long long a[100001];
17 long long b[100001];
18 long long suma[100001],sumb[100001];  //前缀和 
19 long long add[500];  //(前一个1的位置,当前1的位置]中间的连续和最大值 
20 long long vis[500];  //b中值为1的数的位置 
21 long long cnt;
22 long long ans=0;
23 int main()
24 {
25     n=read();
26     for(int i=1;i<=n;i++){a[i]=read();suma[i]=suma[i-1]+a[i];}
27     for(int i=1;i<=n;i++){b[i]=read();sumb[i]=sumb[i-1]+b[i];if(b[i]==1) vis[++cnt]=i;}
28     memset(add,-97,sizeof(add));
29     vis[cnt+1]=n+1; //定义边界 
30     for(int i=1;i<=cnt;i++)
31     {
32         long long temp=0;
33         for(int j=vis[i];j>vis[i-1];j--){temp+=a[j];add[i]=max(add[i],temp);} //找到每两个1之间的最大联系和 
34     }
35     for(int i=1;i<=n;i++) /*找到几个连续段结合起来的最大*/
36     {
37         ans=max(ans,suma[i]*sumb[i]);  //乘积大于2^64是才会用得到高精度。本题不用 
38         for(int j=1;j<=sumb[i]/*有这么多个1*/;j++)ans=max(ans,(add[j]+suma[i]-suma[vis[j]])*(sumb[i]-j+2)); //分段考虑取最大 
39     }
40     for(int i=0;i<=cnt;i++) /*以防某一段(b都是0)全部加起来是最大*/
41     {
42         long long w=0;
43         for(int j=vis[i]+1;j<vis[i+1];j++) {w=max(w+a[j],a[j]);ans=max(ans,w);}  //b全部为0的每段 
44     }
45     printf("%lld",ans);
46     return 0;
47 }
A100

 

以上是关于2017-HDNOIP-提高组1-小鱼干的主要内容,如果未能解决你的问题,请参考以下文章

hdnoip2017

入门4数组

入门4数组

入门4数组

NOIP2016提高组A组7.16大鱼海棠

[NOIP2015] 提高组 洛谷P2680 运输计划