E. Sum Over Zero
E. Sum Over Zero
You are given an array of integers. Consider as a set of segments satisfying the following conditions.
- Each element of should be in form , where and are integers between and , inclusive, and .
- No two segments in intersect with each other. Two segments and intersect if and only if there exists an integer such that and .
- For each in , .
The length of the segment is defined as . is defined as the sum of the lengths of every element in . In a formal way, . Note that if is empty, is .
What is the maximum among all possible ?
Input
The first line contains one integer ().
The next line is followed by integers ().
Output
Print a single integer, the maximum among every possible .
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, can be a possible because and . can also be a possible solution.
Since there does not exist any that satisfies , the answer is .
In the second example, is the only set that satisfies . Since every possible satisfies , the answer is .
In the third example, can only be an empty set, so the answer is .
解题思路
比赛的时候想到了数据结构优化dp的做法,但没写出来。参考树状数组线段树优化最长上升子序列的做法。
这道题本质就是要我们将序列分成若干个互不相交的区间,每个区间的和要求非负,区间的价值就是区间的长度,问在满足条件的划分方案中最大代价可以是多少。
定义状态表示考虑前个数所有合法划分方案中区间长度总和的最大值,根据第个数所在区间长度进行状态划分。如果不包含第个数,那么。如果包含第个数,那么,其中表示前缀和。
很明显这种做法的时间复杂度是,因此需要优化,优化的方法也是很经典。
注意到每次状态转移的时候本质是找到所有满足的,然后有,我们把看作一个整体,有对应,而是一个定值,那么就变成了在所有不超过的中,求一个最大的。也就是询问一个前缀的最大值,因此可以考虑用权值树状数组或者是权值线段树来维护,其中在处存储。
状态转移方程变成了。其中由于的取值范围很大,因此需要离散化。
树状数组做法,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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17110268.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2022-02-10 蹄球