1. 问题理解与暴力解法分析
第一次看到这个题目时,我脑海中立刻浮现出最直观的解法——暴力枚举。对于一个长度为N的数列,我们可以枚举所有可能的子区间[i,j],然后计算每个子区间的和,判断是否是K的倍数。这种方法虽然简单直接,但时间复杂度高达O(N²),当N=1e5时,计算量会达到1e10次操作,这在竞赛中肯定会超时。
让我们具体分析下暴力解法的实现步骤:
- 使用双重循环枚举所有子区间
- 内层循环计算当前子区间的和
- 判断和是否为K的倍数
- 如果是,计数器加1
这个解法的问题在于,对于每个子区间都要重新计算和,存在大量重复计算。比如计算区间[2,5]的和时,其实可以复用之前计算过的[1,5]和[1,1]的和。这让我想到可以使用前缀和来优化。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 前缀和优化思路
前缀和是一种常见的数组预处理技术,它能在O(1)时间内计算任意子区间的和。具体来说,我们定义一个前缀和数组S,其中S[i]表示前i个元素的和(S[0]=0)。那么区间[i,j]的和就可以表示为S[j]-S[i-1]。
应用前缀和优化后,算法流程变为:
- 预处理前缀和数组S(O(N)时间)
- 双重循环枚举所有子区间(O(N²)时间)
- 用S[j]-S[i-1]计算区间和(O(1)时间)
- 判断是否为K的倍数(O(1)时间)
虽然计算单个区间和的时间降到了O(1),但整体时间复杂度仍然是O(N²),对于N=1e5的情况还是不够高效。我们需要进一步优化。
3. 数学推导与余数统计
这里需要一点数学洞察力。我们需要统计满足(S[j]-S[i-1]) mod K == 0的区间数量。根据模运算的性质,这等价于S[j] mod K == S[i-1] mod K。
也就是说,如果我们计算所有前缀和对K取模的结果,那么问题转化为:在这些模值中,有多少对相同的数。因为每对相同的模值都对应一个K倍区间。
具体实现步骤:
- 计算前缀和数组S
- 计算每个S[i] mod K,得到模值数组R
- 统计每个模值出现的次数cnt
- 对于每个模值r,如果有cnt[r]次出现,则可以形成C(cnt[r],2)=cnt[r]*(cnt[r]-1)/2个K倍区间
- 特别地,模值为0的情况可以单独形成区间(从开头到当前位置)
这种算法的时间复杂度是O(N
