E. Sum Over Zero

E. Sum Over Zero

You are given an array a1,a2,,an of n integers. Consider S as a set of segments satisfying the following conditions.

  • Each element of S should be in form [x,y], where x and y are integers between 1 and n, inclusive, and xy.
  • No two segments in S intersect with each other. Two segments [a,b] and [c,d] intersect if and only if there exists an integer x such that axb and cxd.
  • For each [x,y] in S, ax+ax+1++ay0.

The length of the segment [x,y] is defined as yx+1. f(S) is defined as the sum of the lengths of every element in S. In a formal way, f(S)=[x,y]S(yx+1). Note that if S is empty, f(S) is 0.

What is the maximum f(S) among all possible S?

Input

The first line contains one integer n (1n2105).

The next line is followed by n integers a1,a2,,an (109ai109).

Output

Print a single integer, the maximum f(S) among every possible S.

Examples

input

5
3 -3 -2 5 -4

output

4

input

10
5 -2 -4 -6 2 3 -6 5 3 -2

output

9

input

4
-1 -2 -3 -4

output

0

Note

In the first example, S={[1,2],[4,5]} can be a possible S because a1+a2=0 and a4+a5=1. S={[1,4]} can also be a possible solution.

Since there does not exist any S that satisfies f(S)>4, the answer is 4.

In the second example, S={[1,9]} is the only set that satisfies f(S)=9. Since every possible S satisfies f(S)9, the answer is 9.

In the third example, S can only be an empty set, so the answer is 0.

 

解题思路

  比赛的时候想到了数据结构优化dp的做法,但没写出来。参考树状数组/线段树优化最长上升子序列的做法。

  这道题本质就是要我们将序列分成若干个互不相交的区间,每个区间的和要求非负,区间的价值就是区间的长度,问在满足条件的划分方案中最大代价可以是多少。

  定义状态f(i)表示考虑前i个数所有合法划分方案中区间长度总和的最大值,根据第i个数所在区间长度进行状态划分。如果不包含第i个数,那么f(i)=f(i1)。如果包含第i个数,那么f(i)=max0j<i and sisj0{f(j)+ij},其中si=j=1iaj表示前缀和。

  很明显这种做法的时间复杂度是O(n2),因此需要优化,优化的方法也是很经典。

  注意到每次状态转移的时候本质是找到所有满足sjsij,然后有f(i)=max{f(j)+ij},我们把f(j)j看作一个整体,有sj对应f(j)j,而i是一个定值,那么就变成了在所有不超过sisj中,求一个最大的f(j)j。也就是询问一个前缀的最大值,因此可以考虑用权值树状数组或者是权值线段树来维护,其中在sj处存储f(j)j

  状态转移方程变成了f(i)=max{f(i1),query(si)+i}。其中由于si的取值范围很大,因此需要离散化。

  树状数组做法,AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10;
 7 
 8 LL s[N];
 9 LL xs[N], sz;
10 int tr[N];
11 int f[N];
12 
13 int lowbit(int x) {
14     return x & -x;
15 }
16 
17 void add(int x, int c) {
18     for (int i = x; i <= sz; i += lowbit(i)) {
19         tr[i] = max(tr[i], c);
20     }
21 }
22 
23 int query(int x) {
24     int ret = -0x3f3f3f3f;
25     for (int i = x; i; i -= lowbit(i)) {
26         ret = max(ret, tr[i]);
27     }
28     return ret;
29 }
30 
31 int find(LL x) {
32     int l = 1, r = sz;
33     while (l < r) {
34         int mid = l + r >> 1;
35         if (xs[mid] >= x) r = mid;
36         else l = mid + 1;
37     }
38     return l;
39 }
40 
41 int main() {
42     int n;
43     scanf("%d", &n);
44     for (int i = 1; i <= n; i++) {
45         scanf("%lld", s + i);
46         s[i] += s[i - 1];
47         xs[++sz] = s[i];
48     }
49     xs[++sz] = 0;
50     sort(xs + 1, xs + sz + 1);
51     sz = unique(xs + 1, xs + sz + 1) - xs - 1;
52     memset(tr, -0x3f, sizeof(tr));  // 需要先初始化成负无穷,因为维护的是s[i]-i,可能是负数
53     for (int i = 1; i <= n; i++) {
54         add(find(s[i - 1]), f[i - 1] - i + 1);  // 维护前一个结果s[i-1]-(i-1)
55         f[i] = max(f[i - 1], query(find(s[i])) + i);
56     }
57     printf("%d", f[n]);
58     
59     return 0;
60 }
复制代码

  线段树做法,AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10, INF = -0x3f3f3f3f;
 7 
 8 LL s[N];
 9 LL xs[N], sz;
10 int f[N];
11 struct Node {
12     int l, r, maxv;
13 }tr[N * 4];
14 
15 void build(int u, int l, int r) {
16     if (l == r) {
17         tr[u] = {l, r, INF};
18     }
19     else {
20         int mid = l + r >> 1;
21         build(u << 1, l, mid);
22         build(u << 1 | 1, mid + 1, r);
23         tr[u] = {l, r, INF};
24     }
25 }
26 
27 void modify(int u, int x, int c) {
28     if (tr[u].l == tr[u].r) {
29         tr[u].maxv = c;
30     }
31     else {
32         if (x <= tr[u].l + tr[u].r >> 1) modify(u << 1, x, c);
33         else modify(u << 1 | 1, x, c);
34         tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
35     }
36 }
37 
38 int query(int u, int l, int r) {
39     if (tr[u].l >= l && tr[u].r <= r) return tr[u].maxv;
40     int mid = tr[u].l + tr[u].r >> 1, maxv = INF;
41     if (l <= mid) maxv = query(u << 1, l, r);
42     if (r >= mid + 1) maxv = max(maxv, query(u << 1 | 1, l, r));
43     return maxv;
44 }
45 
46 int find(LL x) {
47     int l = 1, r = sz;
48     while (l < r) {
49         int mid = l + r >> 1;
50         if (xs[mid] >= x) r = mid;
51         else l = mid + 1;
52     }
53     return l;
54 }
55 
56 int main() {
57     int n;
58     scanf("%d", &n);
59     for (int i = 1; i <= n; i++) {
60         scanf("%lld", s + i);
61         s[i] += s[i - 1];
62         xs[++sz] = s[i];
63     }
64     xs[++sz] = 0;
65     sort(xs + 1, xs + sz + 1);
66     sz = unique(xs + 1, xs + sz + 1) - xs - 1;
67     build(1, 1, sz);
68     for (int i = 1; i <= n; i++) {
69         modify(1, find(s[i - 1]), f[i - 1] - i + 1);
70         f[i] = max(f[i - 1], query(1, 1, find(s[i])) + i);
71     }
72     printf("%d", f[n]);
73     
74     return 0;
75 }
复制代码

 

参考资料

  Codeforces Round #851 (Div. 2) Editorial:https://codeforces.com/blog/entry/112584

posted @   onlyblues  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-02-10 蹄球
Web Analytics
点击右上角即可分享
微信分享提示