题目描述:给定一个长度为 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
一般的暴力解法:
1.利用前缀和公式求解出s[1],s[2],s[3]....s[n];
for(int i=1;i<=n;i++){
cin>>s[i];
s[i]+=s[i-1];
}
2.两层for循环遍历每一段区间[i,j]
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
int area=s[j]-s[i-1];//计算一段区间的和
if((area%k)==0) res++;//记录最终答案
}
}
很明显,作为蓝桥杯的题目不会这么容易被暴力破解,最终会超时
接下来我们先引入一段知识,假设s[i]是k的倍数,且s[j]是k的倍数,(i<j)那么s[j]-s[i]也是k的倍数且有以下公式:(s[j]-s[i])%k==0 =>> s[j],s[i]同余
也就是说,在 j 之前的 0 ~ j -1当中,只要有 i 满足s[j] ,s[i] 同余,那么s[j]-s[i]所代表的区间就是k倍区间,然后我们就只需要用cnt[N]记录数组记录j之前满足同余条件的i个数,就可以知晓在【1,j】区间当中可以产生多少k倍区间
#include<iostream>
using namespace std;
const int N =1e5+7;
int cnt[N];
typedef long long LL;
LL s[N],res;
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>s[i];
s[i]+=s[i-1];
}//预处理前缀和数组
cnt[0]=1;
//这里要注意边界问题,例如s[i]%k==0 且 s[j]%k==0 ,假设在j之前只有i满足
//s[i]%k==0,那么cnt[0]=1,只能产生1个区间就是s【i,j],但实际上是s[1,i],s[1,j],s[i,j],
//为了避免忽略情况,要对边界处理
for(int i=1;i<=n;i++){
res+=cnt[s[i]%k];
cnt[s[i]%k]++;
}
cout<<res;
}
制作不易,客官点个赞再走啦