题目描述
难度分:1400
输入T(≤104)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(1≤n≤105)和长为n的数组a(−108≤a[i]≤108)。
你需要把a分割成若干非空段,假设分割成了k段,那么得分为:
第一段的元素和×1+第二段的元素和×2+…+第k段的元素和×k
例如[1,2,3,4]分成两段[1,2]和[3,4],得分为(1+2)×1+(3+4)×2
输出最大得分。
输入样例
4
6
1 -3 7 -6 2 5
4
2 9 -5 -3
8
-3 -4 2 -5 1 10 17 23
1
830
输出样例
32
4
343
830
算法
后缀和
本质上题目所给的操作就是找一些后缀和累加起来(并且第一个后缀和s1=Σni=1a[i]是必选的),使得这个累加和最大。
下面解释一下为什么是选一些后缀相加,因为第k个后缀其实是第k−1个后缀的后缀,所以在加过第k个后缀的情况下,再把第k−1个后缀加上去就相当于第k个后缀加了两次的。因此,第k−1个后缀中不在第k个后缀中的元素就只加了一次,这样就相当于前面一个子数组加了一次,后面一个子数组加了两次。依此类推累加到第一个后缀,就能得到题目想要求的东西。
那么我们找到除了s1之外所有大于0的后缀加起来就可以了,它就是题目要求的最大累加和。
复杂度分析
时间复杂度
遍历一遍数组就可以求得答案,因此时间复杂度为O(n)。
空间复杂度
除了输入的原始数组a,仅使用了有限几个变量,因此额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200010;
int T, n, a[N];
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
LL sufsum = 0, ans = 0;
for(int i = n; i >= 1; i--) {
sufsum += a[i];
if(i > 1 && sufsum > 0) {
ans += sufsum;
}
}
ans += sufsum;
printf("%lld\n", ans);
}
return 0;
}