1. 程式人生 > >2017-HDNOIP-提高組1-小魚幹

2017-HDNOIP-提高組1-小魚幹

false stream image 不一定 最大值 png pla mem long

小魚幹

【問題描述】

小喵喵有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-小魚幹