题目描述
给定一个长度为 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
运行限制
- 最大运行时间:2s
- 最大运行内存: 256M
题解:
方法一:
首先最普遍的方法是暴力算法,不停的遍历,通过条件判断进行计数。
#inlcude<bits/stdc++.h>
using namespace std;
int n, k;
int a[100000];
int result = 0;
int main()
{cin >> n >> k;for(int i=0; i<n; i++){cin >> a[i];}for(int i=0; i<n; i++){if(a[i] % k == 0){result++;}for(int j=i+1; j<n; j++){a[i] = a[i] + a[j];if(a[i] % k == 0){result++;}}}cout << result;return 0;
}
但是该方法的时间复杂度较高,两层for循环O(n*n)
方法二:
利用前缀和算法实现,前缀和就是数组的前 i 项之和。
假设:n=5, k=2。
则数组a[] = {1, 2, 3, 4, 5}的前置和为:
原数列:1,2,3,4,5
前缀和:1,3,6,10,15
针对该前缀和对k进行取余:
取余后:1,1,0,0,1
余数相同的两个元素之间的区间即为K倍区间,例如mod[2]==mod[5],则区间[i+1, j]也就是[3,5]是K倍区间。也可以这么理解区间(2,5]为K倍区间,该区间左开右闭。
这个理解后,就可以认为余数相同的两个数之间的区间即为K倍区间。因此对相同余数的两个元素进行排列组合。为C2 2加上C3 2(3为下标,2为上标),结果为1+3=4;
但是由于该区间是左开右闭的,因此要加上取余为0的个数。才构成完整的结果。
也就是先通过排列组合计算出中间区间为K倍区间的个数(4个):
2345,345,2,4
再加上取余为0的个数(2个):
123,1234
实际结果为6个。
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long input[100000], mod[100000] = {0};
long long add[100000] = {0}, sum = 0;
int main()
{// 请在此输入您的代码int n, k;long long reslut = 0;cin >> n >> k;//输入数列for(int i = 0; i < n; i++){cin >> input[i];}//对数列每个元素求前缀和存入sum,并对k进行取余存入modfor(int i = 0; i < n; i++){// 当前索引的前缀和sum += input[i];// 对该前缀和对k进行取余mod[i] = sum % k;// 对该索引前缀和取余后的数值出现次数进行累加add[mod[i]]++;}//计算结果for(int i = 0; i < n; i++){// 针对每种取余的值,进行计算排列组合,最后相加reslut += add[i] * (add[i]-1) / 2;}// 由于上面是左开右闭区间,缺少了一部分cout << reslut + add[0] << endl;return 0;
}