http://www.gdfzoj.com/oj/contest/159/problems/3
Problem 704: 数组操作
Time Limit: 1000 ms Memory Limit: 262144 KB
Problem Description
给出n个数组,每个数组有Li个整数,将数组编号为1,2,3,…,n,然后给出m个数,每个数均为之前数组的编号,将这些数组按照给出的编号顺序连接起来,形成新的数组,比如:给出以下三个数组[1,6,-2],[3,3],[-5,1],然后给出编号2 3 1 3,则新数组为[3,3,-5,1,1,6,-2,-5,1],现在请问新数组的最大连续子序列和是多少。
Input
第一行两个正整数n,m。
接下来n行,每行第一个数为Li,之后Li个整数。
最后一行有m个正整数,表示连接数组顺序的编号。
【数据范围】
对于10%的数据,形成的数组长度不超过100000
对于100%的数据,1 <= n <= 50,1 <= Li <= 5000,1 <= m <= 250000,每个数组中的数绝对值在1000以内
Output
一行一个数,表示最大连续子序列和。
Sample Input
3 4
3 1 6 -2
2 3 3
2 -5 1
2 3 1 3
Sample Output
9
[3,3,-5,1,1,6,-2,-5,1],红色部分为最大和的连续子序列。
Problem Source
2017提高组联赛训练7.10
分析
如果暴力的话只能拿三十分(亲自试验tle)
所以我们可以预处理处每个数组的左区间和,最大连续子段和,右区间和,区间和
然后O(n)两次动态规划
然后每个区间统计答案即可
注:子段和至少要选取一个元素,即如果询问的区间全部都是负数,那么输出的答案为最小的那个负数的值
吐槽一下gfoj数据太弱,非负都a了
ac代码
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 60
#define M 250010
#define INF 9000000000000000000
using namespace std;
typedef long long LL;
int a[M],n,m,s;
LL ans,f[M][2],sum[N],sub[N],rma[N],lma[N],num;
int main(){
freopen("data.txt","r",stdin);
memset(lma,-0x7f,sizeof(lma));
memset(rma,-0x7f,sizeof(rma));
memset(sub,-0x7f,sizeof(sub));
ans=-INF;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&s);
for(int j=1;j<=s;j++)scanf("%d",&a[j]);
num=0;
for(int j=1;j<=s;j++){
num+=a[j];
lma[i]=max(lma[i],num);
}
num=0;
for(int j=s;j>=1;j--){
num+=a[j];
rma[i]=max(rma[i],num);
}
num=0;
for(int j=1;j<=s;j++){
sum[i]+=a[j];
num=max(num+a[j],(LL)a[j]);
sub[i]=max(sub[i],num);
}
// cout<<lma[i]<<' '<<sub[i]<<' '<<rma[i]<<' '<<sum[i]<<'\n';
}
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)f[i][0]=max(f[i-1][0]+sum[a[i]],rma[a[i]]);
for(int i=m;i>=1;i--)f[i][1]=max(f[i+1][1]+sum[a[i]],lma[a[i]]);
for(int i=0;i<=m;i++)ans=max(ans,max(sub[a[i]],f[i][0]+f[i+1][1]));
cout<<ans;
}