1 条题解
-
0
题意理解
我们有一个长度为 ( N ) 的整数数组 ( c[1..N] ),以及 ( Q ) 个查询,每个查询给出区间 ([L_i, R_i])。
对于每个查询,我们要找出在区间 ([L_i, R_i]) 内,最多能选出多少个不相交的、和为 0 的子数组。
- 不相交:子数组之间没有公共元素(但可以相邻)。
- 一些元素可以不属于任何选出的子数组。
样例分析
样例:
数组: 1 2 -3 0 1 -4 3 2 -1 1 查询1: [1,10] → 答案4 查询2: [1,5] → 答案2 查询3: [2,9] → 答案2
问题转化
设前缀和: [ s[i] = \sum_{k=1}^i c[k], \quad s[0] = 0 ] 那么子数组 ([l, r]) 的和为 0 等价于: [ s[r] = s[l-1] ]
问题变成:在区间 ([L, R]) 内,找出尽可能多的不相交的配对 ((l-1, r)) 满足 (s[l-1] = s[r]),并且这些区间不重叠。
贪心策略
经典贪心:从左到右扫描,一旦发现一个位置 (r) 使得存在某个 (l-1 \ge L-1) 且 (s[r] = s[l-1]) 并且区间 ([l, r]) 与已选区段不重叠,就选取它。
这等价于:在区间 ([L-1, R]) 的前缀和序列中,找尽可能多的不相交的相等数对。
数据结构解法思路
代码的核心思路是:
- 预处理前缀和,离散化。
- 找出所有和为 0 的子数组(即 (s[r] = s[l-1]) 的 ((l, r))),并按照某种顺序存储。
- 贪心选择:对于每个右端点,选择最晚可用的左端点(不重叠)。
- 回答查询:用某种数据结构快速得到区间 ([L, R]) 内能选出的最多子数组数。
代码结构分析
1. 前缀和与离散化
ll sum = 0; S[n + 1] = 0; for (int i = 1; i <= n; i++) { cin >> c[i]; sum += c[i]; S[i] = sum; } sort(S + 1, S + 2 + n); int len = unique(S + 1, S + 1 + len) - (S + 1);这里
S数组用来存前缀和,并离散化。
2. 建立“下一个相同前缀和”的关系
sum = 0; for (int i = 1; i <= n; i++) { sum += c[i]; int id = lower_bound(S + 1, S + 1 + len, sum) - S; if (sz[id]) { dfn[i] = sz[id]; // 记录与当前前缀和相等的上一个位置+1 sz[id] = i + 1; } else { dfn[i] = n + 1; sz[id] = i + 1; } }这里
dfn[i]表示:对于右端点 (i),上一个相同前缀和的位置是dfn[i] - 1,所以区间是[dfn[i], i]和为 0。
3. 贪心选择区间
int lst = 0; for (int i = 1; i <= n; i++) { if (dfn[i] == n + 1 || dfn[i] <= lst) { dfn[i] = 0; // 不可用 continue; } lst = dfn[i]; // 选择这个区间,更新 lst }这里
lst表示上一个选择的区间的左端点,为了保证不重叠,必须dfn[i] > lst。
4. 建立树结构
之后代码构建了一个树结构,
head和nxt是邻接表,add(u, v)表示v是u的孩子。这个树是按照区间包含关系建立的:每个区间
[dfn[i], i]作为节点,它的父节点是包含它的下一个区间。
5. DFS 序与 BIT
用 DFS 序将树结构线性化,然后用树状数组(BIT)维护某些信息,用于快速回答查询。
查询时,对于区间 ([L, R]),找到在 DFS 序中对应的节点,用 BIT 查询覆盖的区间数量。
算法标签
- 前缀和 + 哈希 (Prefix Sum + Hashing)
- 离散化 (Discretization)
- 贪心选择 (Greedy Selection)
- 树状数组 (Fenwick Tree / BIT)
- DFS 序 (DFS Order)
- 区间查询优化 (Range Query Optimization)
核心思想总结
- 将“和为 0 的子数组”转化为“前缀和相等的位置对”。
- 用贪心法选择不重叠的区间:按右端点排序,每次选左端点尽量靠右的。
- 将选出的区间建成树结构,利用 DFS 序和 BIT 快速回答任意区间 ([L, R]) 内能选出的最多子数组数。
- 时间复杂度 (O((N+Q) \log N)),适合 (N, Q \le 4\times 10^5)。
样例验证
对于样例:
- 数组
1 2 -3 0 1 -4 3 2 -1 1 - 前缀和
1 3 0 0 1 -3 0 2 1 2 - 和为 0 的子数组有:
[3,3],[1,3],[4,4],[3,4],[6,6],[3,7],[7,7],[9,9],[8,10]等 - 贪心选择后得到最优不重叠集合。
查询
[1,10]能选 4 个,例如[1,3],[4,4],[6,7],[8,10]。
这样,我们就用高效的数据结构完成了大规模数据的查询。
- 1
信息
- ID
- 3510
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者