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
标签:10,int,Over,mid,possible,leq,Zero,Sum,LL From: https://www.cnblogs.com/onlyblues/p/17110268.html