【前缀和】蓝桥杯试题 k倍区间

题目描述

OJ地址 http://lx.lanqiao.cn/problem.page?gpid=T444

给定一个长度为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倍区间的数目。

样例输入1:

5 2
1
2
3
4
5

样例输出1:

6

思路:

假设前缀和数组为a,如果 a[i] % k == 1,a[j] % k == 1,那么i到 j-1肯定构成一个k倍区间了,a[i]和a[j]对k取模余数相同,那么他们两个做差的话,余数部分肯定减没了,剩下的部分一定是K的倍数。

用一个数组b来记录a[i]出现的次数,例如,之前a[i]出现了5次,那么第六次再出现a[i]的时候,第六次的这个a[i]可以和前边5个a[i]分别构成一个k倍区间,这就构成了5个了,然后b[a[i]]++,也就是记录a[i]出现的次数增加1次。

另外要注意的是,前缀和对k取模等于0的时候,比如a[i] % k == 0,有两种情况,第一种是i和前边说的一样,之前出现了几次0,就会构成几个k倍区间。另一种就是,a[i]本身就是一个k倍区间,即 数组中,前i个数加起来就是一个k倍区间。所以,遇到模k等于0的时候要在答案中多记录一次。

代码

#include <bits/stdc++.h>

using namespace std;

long long a[100005],sum[100005];
int n;


int main(){
    
    
	int k;
	long long ans = 0;
	cin >> n >> k;
	
	for(int i = 1;i <= n; i++){
    
    
		long long s;
		scanf("%d",&s);
		sum[i] = (sum[i-1] + s) % k;
		if(sum[i] == 0) ans++;
		
		ans = ans + a[sum[i]];   // 这个余数,原来出现过5次,那又出现了的话(第六次),就可以构成5个k倍区间 
		a[sum[i]]++;    // 出现次数增加 
		
	}
	cout << ans << endl; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/u014117943/article/details/104642930
今日推荐