k倍区间
给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列Ai, Ai+1, … Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出
输出一个整数,代表K倍区间的数目。
例如,
输入:
5 2
1
2
3
4
5
程序应该输出:
6
解题思路
首先没有认真细想,想到的会是三层for循环遍历所有的子序列,但是这样的时间复杂度就会只能跑过前两个测试用例.这时我们就要想办法去优化。
最容易想到的就是两个for循环的优化,首先在刚开始输入值的时候,就把前i项的和存起来,这样就有了所有前i项的和,然后再去用两个for循环嵌套,遍历的是,任选两个的前i项作差,就是中间的那几项和,然后再去判断。但是这样仍然超时。那么就再想,能不能不嵌套循环就写出来。
这里需要注意到一个小知识,两个数对同一个数取模得到的值一样时,那么这两个数作差,就能被这个整数整除,比如:16%3=1,7%3=1,那么(16-7)%3 = 0。根据这个性质我们就可以开始进行优化了,将之前所求的前i项的和,对k取模,映射到一个数组里面,比如上述测试用例。求和:1,3,6,10,15.取模:1,1,0,0,1;取模得到1的数共有三个,也就是从这三个中选出来两个作差就可以被整除,也就是C(3,2),取模得到0的数共有2个(正好被整除,特殊),作差可以作出一个,然后再加上两个正好被整除的,也就是1+2,但是这两种情况并不能归为一类,需要分类,那么就再优化一下,让他归到一类,C(m,2)所代表的不就是从1+2+...+m-1,那么让0多一个,不就依然可以看成从中选两个了。
下面是ac代码
#include <stdio.h>
#include <stdlib.h>
int A[100010] = {0};//这里我们要放取模后的值
long long int S[100010];//这里存的是前i项的和
int main()
{
S[0] = 0;//因为我们要求和,避免麻烦。就从第二项开始记,依次加上前一项,这样第一项置为0并没有影响
A[0] = 1;//这里就是将0的值增加了一个,统一形式
int n,k;
scanf("%d %d",&n,&k);
for(int i = 1;i<=n;i++)
{
int a;
scanf("%d",&a);
S[i] = S[i-1]+a;//求和
A[S[i]%k]++;//取模后的值映射到这个数组里
}
long long int res = 0;
for(int i = 0;i<k;i++)
{
res += (long long int)A[i]*(A[i]-1)/2;//这里就是C(m,2)
}
printf("%lld",res);
return 0;
}