Bootstrap

Acwing 1230. K倍区间

1230. 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
难度:中等
时/空限制:1s / 64MB
总通过数:2925
总尝试数:7508
来源:第八届蓝桥杯省赛C++B组,第八届蓝桥杯省赛JAVAB组
算法标签 前缀和

思路:

通过题目不难分析,题目的大意就是求有N个元素的数组中子序列是K的倍数的有几个。我们可以直接用二重循环直接暴力得到答案,但是分析得出 用而重构循环遍历时间复杂度为O(n^2) ,这里N为 10 ^5 ,所以要进行 10 ^10 的计算量,这当然是超出1s的,所以我们用前缀和的方法可以把时间复杂度降到O(n)。
根据条件 (我们这里把算出来的前缀和存储在sum数组中)我们可以得到题目中满足的条件:

(sum[j]-sum[i-1])%k == 0  

即:sum[j] %k == sum[i-1] % k
所以我们可知:只要两个前缀和对k取余结果相等就可以配凑处一对i j ,组成满足条件的区间。 另外如果sum[j]==0 , i-1 ==0 时 ,这也可以组成一种情况。
综上所述:

  • 首先得到1 ~ N的前缀和数组
  • 前缀和数组对k取余
  • 计算值相等的个数为n 从这n个种取两个 然后加上 值为0 的个数就是最后结果。

代码:

#include <iostream>
#include <cmath>
using namespace std;
const int n=1e5+10;
int N,k;
int sum[n];
long long res[n];
int main(){
	int ans=0;
	cin>>N>>k;
	for(int i=1;i<=N;i++){
		scanf("%d",&sum[i]);
		sum[i]=(sum[i]+sum[i-1])%k;
		ans+=res[sum[i]]; // res数组对k取余后相同值有n个 ,从0开始加到n-1  得到从n的种选择2个的种数
		res[sum[i]]++;
	}
	cout<<ans+res[0]<<endl;  // 最后加上对k取余为0的情况数
}

;