原题链接
给定一个长度为$N$的数列,$A_1,A_2,…A_N$,如果其中一段连续的子序列 $A_i,A_{i+1},…A_j$之和是$K$的倍数,我们就称这个区间$[i,j]$是$K$倍区间。

你能求出数列中总共有多少个$K$倍区间吗?

分析

暴力做法

套两个循环,每个循环使用sum去求和然后取余,时间复杂度为$O(n^3)$

前缀和

使用前缀和可以优化到重复的求和部分,但是这样也需要两层循环遍历,时间复杂度为$O(n^2)$

优化前缀和

在基本前缀和的两层循环中,判断某一范围之和是否余数为0实际上是使用$a[i]-a[j] \ mod\ K \equiv 0$来判断的,再进一步思考实际上就是$a[i]$和$a[j]$同余才可以。这样完全可以再开辟一个数组,用来存放$a[i]\ mod\ K$的值,这样只需要遍历这个数组一遍即可,时间复杂度为$O(n)$。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100001;
LL a[N], n, k;
int cnt[N];
int main() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
a[i] = a[i-1] + x;
}
LL res = 0;
for(int i = 0; i <= n; ++i) {
res += (LL)cnt[a[i] % k];
cnt[a[i] % k]++;
}
printf("%lld", res);
return 0;
}

注意点

观察数据范围,判断是否会爆int,是否需要使用long long来储存。

反思:还是不会分析问题,同时过于依赖代码评测的报错。