题意:寻找所有区间和是k的倍数的区间
一眼过去就可以想到前缀和思想,让查询区间的时间复杂度变成O(1)
1.我们枚举区间的左右端点
枚举区间的左右端点,检查是不是k的倍数,时间复杂度可以做到O(n^2),但是过不了所有的点,需要继续优化
2.查找性质优化
那么通过这个性质,我们可以参考dp的思想,记录所有区间%k后的值,让他们的cnt++
那么我们只需要遍历一遍区间就可以了
如果一个前缀和前面有n个前缀和%k后的值,那么这个点作为右端点的k倍区间就有n个,我们可以边计数边计算前缀和来获得答案
还要注意的是可以一个区间(0,i)他本身的区间和就是一个k倍区间,不能忘记算他
综上
k倍区间有两种 1.s[r]%k==s[l-1]%k 通过dp数组计数前面和他前缀和相同的数组
2.s[r]%k==0 那么特判一下,他的左端点有一个是0
我们用sum数组计算前缀和,cnt数组计算前面有多少区间取模后和当前区间取模的值相同
for (int i = 1;i <= n;i++)
{
cin >> sum[i];
}
for (int i = 1;i <= n;i++)
{
sum[i] += sum[i - 1];
if (sum[i] % k == 0) res++;
res += cnt[sum[i] % k];
cnt[sum[i] % k]++;
}
注意需要特判一下0~i区间是否是k倍区间
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100005;
int sum[N],cnt[N];
int main()
{
int n,k;
cin>>n>>k;
long long res=0;
for(int i=1;i<=n;i++)
cin>>sum[i];
for(int i=1;i<=n;i++)
{
sum[i]+=sum[i-1]%k; //这里也可以不取模,但是要开ll,不然会越界
if(sum[i]%k==0) res++;
res+=cnt[sum[i]%k];
cnt[sum[i]%k]++;
}
cout<<res;
}