给定一个长度为 n n n 的正整数序列 ( 2 ≤ n ≤ 2 × 1 0 5 ) (2\le n\le 2\times 10^5) (2≤n≤2×105) a i a_i ai( − 1 0 9 ≤ a i ≤ 1 0 9 -10^9\le a_i\le 10^9 −109≤ai≤109, a i ≠ 0 a_i\ne 0 ai=0)
求至少需要插入多少个数(可以为 R R R 上的任意大小的实数),使得该序列任意子区间的和不为 0 0 0
联系到前缀和思想,子区间和 S u m l , r Sum_{l,r} Suml,r 可以表示为 S r − S l − 1 S_r-S_{l-1} Sr−Sl−1
则对于子区间和 S u m l , r = 0 Sum_{l,r}=0 Suml,r=0 的情形,即等价于 S r = S l − 1 S_r=S_{l-1} Sr=Sl−1,以及 l = 1 , S r = 0 l=1,S_r=0 l=1,Sr=0 的情形
相对原始的想法:对前缀和数组判重,以及判断 S i = 0 S_i=0 Si=0 的元素的个数
若在 i i i 处插入一个数 x x x,将会使得前缀和序列 S i → S n S_i\to S_n Si→Sn 均 + x +x +x
对于 i → n i\to n i→n,其相对大小不变
例: . . . , x , − 1 , 1 ...,x,-1,1 ...,x,−1,1:插入后, S i + 1 ≠ S i + 2 S_{i+1}\ne S_{i+2} Si+1=Si+2,但 − 1 + 1 = 0 -1+1=0 −1+1=0由此,不能直接维护整个序列的前缀和数组,而是应当维护当前段的前缀和(即截止判重时的一段数,此时,该前缀和更新为当前位置被判重的数),然后沿用判重操作同时,插入后的前一段已经不影响后一段的结果(插入一个近乎无穷大的数,后一段的前缀和必然远大于前一段,不可能相等;同时计算机也不支持这样近乎无穷大的数),因此可以将用于判重的数据结构清空维护当前段的前缀和 c u r cur cur,并使用 s e t set set 或 u n o r d e r e d _ s e t unordered\_set unordered_set 维护当前出现过的 c u r cur cur 的值
(可以将 0 0 0 提前插入,规避特判,简化代码)
每当出现重复的值,则更新需要插入的数的个数 a n s ans ans,并清空 s e t set set 或 u n o r d e r e d _ s e t unordered\_set unordered_set ,将 c u r cur cur 更新为 a i a_i ai,再将 0 0 0 和 a i a_i ai 插入对应的数据结构(”性质“中对当前段的分析)
大概是比较常见的区间转化为前缀和的分析技巧
(印象比较深刻的是在解 AcWing 239. 奇偶游戏 的时候,如果没有此题的启发可能都做不动)
然而自己做的时候并没有分析出维护的是判重前后每一段的前缀和,直接套在整体的前缀和上做,然后来回WA
只是分析到了需要判重和判 0 0 0 的时候,如果没有此题的启发可能都做不动)
P S PS PS:写 c f cf cf 的题解感觉需要把整个问题彻底想清楚才行,光看 e d ed ed 也是似懂非懂,写题解花了不少时间,不过终于把整个问题梳理清楚了)
