E. Sum Over Zero

E. Sum Over Zero

You are given an array $a_1, a_2, \ldots, a_n$ 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 $x \leq y$.
  • 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 $a \leq x \leq b$ and $c \leq x \leq d$.
  • For each $[x, y]$ in $S$, $a_x+a_{x+1}+ \ldots +a_y \geq 0$.

The length of the segment $[x, y]$ is defined as $y-x+1$. $f(S)$ is defined as the sum of the lengths of every element in $S$. In a formal way, $f(S) = \sum_{[x, y] \in S} (y - x + 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$ ($1 \leq n \leq 2 \cdot 10^5$).

The next line is followed by $n$ integers $a_1, a_2, \ldots, a_n$ ($-10^9 \leq a_i \leq 10^9$).

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 $a_1+a_2=0$ and $a_4+a_5=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) \leq 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(i-1)$。如果包含第$i$个数,那么$f(i) = \max\limits_{0 \leq j < i \ \text{and} \ s_{i} - s_{j} \geq 0} \{ f(j) + i - j \}$,其中$s_i = \sum\limits_{j = 1}^{i}{a_j}$表示前缀和。

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

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

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

  树状数组做法,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 @ 2023-02-10 20:54  onlyblues  阅读(78)  评论(0编辑  收藏  举报
Web Analytics