问题解释:
在本题中采用暴力解法超时过于离谱,所以需要一步步优化。
在最基本的暴力里,时间复杂度大约是O(n^3),根据题目的数据范围,我们大致可以看出能通过的时间复杂度大约是O(n)左右,那么就需要降幂。
首先用前缀和将最里层循环取代,这个大家应该都会。
其次比较难想的一点就是取模。
在暴力做法里,我们代码大致是这样
```
for(int r=1;r<=n;r++){
for(int l=1;l<=r;l++){
if(!(s[r]-s[l-1])%k){
res++;
}
}
}
```
将代码仔细分析s[r]-s[l-1])%k,这句代码的意思不就是找两个s数组里的数,他们的模k的余数相同吗?那我们不就可以直接统计我们当前的s中有几个数的余数相同,然后得到答案吗。
故,代码如下:
#### C++ 代码
```
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
LL s[N];//前缀和数组
LL w[N];//存余数
int main(){
int cnt,k;
cin>>cnt>>k;
//处理前缀和
for(int i=1;i<=cnt;i++){
LL a;
scanf("%lld",&a);
s[i]=s[i-1]+a;
}
//枚举区间的左右端点
w[0]=1;//余数是0 的数已经存在了,
LL res=0;
for(int i=1;i<=cnt;i++){
res+=w[s[i]%k];//w[i]存的是余数是i的数的个数
w[s[i]%k]++;//++的原因是余数为s[i%k]的区间又多一个 ,本质上来讲是求w[i]上有几个相同余数的数
}
cout<<res<<endl;
return 0;
}
```