给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj之和是 K 的倍数,我们就称这个区间 [i,j]是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K 倍区间的数目。
数据范围
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6
题意:
求给定数列有多少个区间是k的倍数
分析:
因为是区间嘛,所以我开始的第一思路是先求出数列的去前缀和,然后枚举区间[i,j]是否满足条件,没想到居然超时了。
因此我们考虑如何进行优化。
首先,我们需要先知道几个点:
一:如果区间l~r 是k的倍数(sum[r]-sum[l-1])%k==0,那么sum[l-1]%k==sum[r]%k,
特别地,如果sum[i]%k==0,那么区间[1,i]是k的倍数
明白了这一点,我们就可以进行优化了。
首先,求出数组的前缀和取模,即sum[i]不再表示前缀和,而是前缀和的取模,意思是1~i的和%k
然后,用res[sum[i]]存储sum[i]出现的次数
最后,ans表示答案
具体这些变量的作用请看代码注释
#include <iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N],res[N],sum[N];
long long ans;
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;++i){
cin>>a[i];
//sum[i]表示区间1~i的和模以k的值,用前一个模后的值加上a[i]可得到,具体证明就不给了
//如果不想这样子也可以先求前缀和,再单独对其取模,只不过这样的话数组得为long long
sum[i]=(sum[i-1]+a[i])%k;
//res[sum[i]]表示sum[i]出现的次数,举个例子,如果sum[i]等于3,而res[sum[i]]=4,
//说明这次的3可以与前面4个三组合成k倍区间,那么ans就应该再加上4,之后res[sum[i]]就++
ans+=res[sum[i]];
res[sum[i]]++;
}
//由于res[sum[i]]统计的是sum[i]相同的个数,如果res[sum[i]]==1是不加入答案的,但如果
//sum[i]==0,则自己可以成为k倍区间,故再加上res[0].
ans+=res[0];
cout<<ans;
return 0;
}