有帮助就点个赞呗👍
1.题目
K倍区间
给定一个长度为 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
2.思路
借鉴y总思路
思路:暴力求解O(n3) →前缀和优化O(n2)→数学知识及空间换时间O(n)
难点:
1.y总怎么化简成求:当R固定时,在L在[0,R-1]之间,所有sum[R]与sum[L]余数相等的数的个数
因为,按前缀和知识,(sum[R]-sum[L-1])%k==0,先将L-1换成L,随之L就是在[0,R-1]之间,
然后把括号拆开再移项,即可化简成sum[R]%k==sum[L]%k.
2.为什么还要加cnt[0]=1;
下面代码有备注,直接回答字太多
3.如何做到时间换空间
理解sum[R]%k==sum[L]%k这一行,y总直接将相同余数放入cnt数组中
因此无序判断,直接索引,即相加即可
代码体现在 res+=cnt[sum[i]%k]; 这一行
3.代码
#include<iostream>
using namespace std;
const int N=1000009;
typedef long long LL ;
LL sum[N];
LL cnt[N];
int n,k;
int main()
{
cin>> n >> k;
for(int i=1;i<=n;i++)
{
scanf("%lld",&sum[i]);
sum[i]=sum[i]+sum[i-1];
}
/*调试用,打印各求和区间的范围
for(int i=1;i<=n;i++)
{
for(int j=0;j<=i-1;j++)
printf("[%d,%d] ",j+1,i);
cout<<endl;
}
cout<<endl;
*/
LL res=0;
cnt[0]=1;
/*
再看一遍y总说的:
当R固定时,在L在[0,R-1]之间,所有sum[R]与sum[L]余数相等的数的个
数
因为y总是先统计,再累加,然后代码是从i=1开始循环,所以实际上res这一行执行了n次,
而cnt执行了虽然也是n次,但是第n次时,统计的数已经没有意义了,所以只执行了
[1,n-1]的有效范围,0被忽视掉了!
所以必须加上cnt[0]=1这一句才满足“在L在[0,R-1]之间”(R指右边界,姑且认为是n)
如果觉得太麻烦,可以试试先累加,再统计,便于理解,俩行已经在下面注释掉
*/
for(int i=1;i<=n;i++)
{
res+=cnt[sum[i]%k];//先统计 出现与当前值求余的结果相同的元素个数
cnt[sum[i]%k]++;//再累加当前的值求余的结果,并储存进cnt中
//下面解法是先累加在统计,不用考虑cnt[]的影响
//即按y总的意思: 当R固定时,在L在[1,R]之间,所有sum[R]与sum[L-1]余数相等的数的个数
// cnt[sum[i-1]%k]++;//先累加,sum[i-1]代表sum[L-1]的余数,cnt[sum[i-1]%k]代表其余数的个数
// res+=cnt[sum[i]%k];//再统计,直接统计sum[i]就行了
//即判断所有sum[R]与sum[L]余数相等的数的个数
/*调试用:打印各个步骤的值
for(int i=0;i<k;i++)
printf("%d ",cnt[i]);
printf("res=%d\n",res);
*/
}
printf("%lld",res);
}
4.编译环境
难得写个笔记,点个赞再走呗~
肥肠感谢!
😁