[CP / AtCoder] abc367 D - Pedometer

以后必提前 register……昨天 abc 来晚了 20 分钟,被迫 unrated,没想到却拿到了自己的历史最好成绩 2535 名。


D - Pedometer

最近发现 ABC 的 D 有好多 counting 的题目——我最喜欢这种题目了!

这类题目的特征是,如果暴力统计结果的话,时间复杂度一般是 O(n2) 或更高,但如果我们能利用其中隐含的一些美妙性质,使用合理的数据结构,就能同时统计一大批数据而不是只统计其中一组,从而将时间复杂度降低到 O(nlogn) 甚至 O(n),可谓化腐朽为神奇。

D - Pedometer 便是其中之一。

这道题我初见时并没有做出来。在尝试了约一小时无果后,我决定研究题解。最近时间不多,没法像以前一样为了一道题硬想几个小时,虽然快乐但低效。既然没有思路,我认为自己大概率是缺乏解决此类问题所需的 technique,事实证明确实如此。

除了学到了新的计数技巧之外,我还认识了一种更优雅的处理环的方式,先从后者讲起。

一切还要追溯到 CF971-F,一道 1700* 的题。这道题需要对循环移位后的数组进行前缀和查询,因为我没有接触过相关题目,在比赛时花了大量时间在草稿纸上推导如何根据循环移位的距离将区间端点映射到前缀和数组上去,然后写出了极其扭曲的代码(下图)。后来看了别人的题解,发现仅仅是将前缀和数组复制了一份连在一起,就能大大简化计算过程!

image

这就是环的处理方式。考虑和上面类似的情况——查询环上的前缀和。不可避免地,我们会遇到起始端点的索引大于终止端点的情况。如果依然使用最简单的前缀和,就需要将区间 [L, R] 分隔成两段,一段是 [L, N],另一段是 [1, R]。

如果我们将前缀和数组再复制一遍然后连接在原来的前缀和数组后面,那么就不需要分隔区间了,结果就是 [L, R + N]。

当然,对于这种简单的情境来说,两种做法的区别不是很大。再看本题。

本题给我们一个环。我们需要统计该环上所有的满足长度非零且元素和为 M 的倍数的区间。显然,暴力统计的话需要 O(n2) 的时间。

我想了很久,始终无法从暴力做法中跳出来。我想,每个区间和都是一个整数,那么该问题是否与 “输入许多(数量最大可达 4e10)随机的整数(<= 1e9),统计可以被 M 整除的整数的个数” 等同?

不行。我无法直接证明这一点,但我可以用反证法。如果它们等价,那么本题便做不出来了。我们无法在不对所有的整数进行 “观测” 的前提下得到最终答案,否则就会丢失信息,因此时间复杂度至少是 O(n),又由于输入整数的数量可能很大,算法必然超时。

既然如此,原问题一定有某种可以利用的特殊性质,它能提供更多的信息,使得我们无需考察所有的前缀和。所以是什么呢?我在这里卡住了。

……

同余。我想到了利用前缀和来加速区间和的计算,但我们并不需要真的通过计算区间和来判断该区间和是否被 M 整除。

如果对前缀和数组 pre 的每个元素做一次 MOD M,得到模前缀和数组 prem ,那么某区间 [L, R] 内元素的和 pre[R] - pre[L-1] 可以被 M 整除,就等价于 prem[R] == prem[L-1]

为什么后者更便于统计?因为比较是否相等这一操作,比作差的约束要弱得多。只需要维护一个 cnt 数组,我们就能统计相等元素的个数。

剩余的就是一些实现上的细节了,请允许我略去。

def solve():
n, m = map(int, input().split())
v = list(map(int, input().split()))
pre = [0] * (2 * n + 1)
prem = [0] * (2 * n + 1)
for i in range(2 * n):
pre[i + 1] = pre[i] + v[i % n]
prem[i + 1] = pre[i + 1] % m
ans = 0
cnt = [0] * m
for i in range(1, 2 * n + 1):
if i >= n + 1:
ans += cnt[prem[i]]
cnt[prem[i]] += 1
if i >= n:
cnt[prem[i - n + 1]] -= 1
print(ans)

这里 prem 数组的计算实际上可以简化,因为:

(A+B) % M=(A % M+B % M) % M

所以能直接写成 prem[i + 1] = (prem[i] + v[i % n]) % m

posted @   ZXPrism  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示