k倍区间
题目描述
给定一个长度为 N
的数列 A₁, A₂, ⋯, Aₙ
,如果其中一段连续的子序列 Aᵢ, Aᵢ₊₁, ⋯, Aⱼ
(i ≤ j
) 之和是 K
的倍数,我们就称这个区间 [i, j]
是 K 倍区间。
请你求出数列中总共有多少个 K 倍区间。
输入描述
- 第一行包含两个整数
N
和K
(1 ≤ N, K ≤ 10⁵
)。 - 接下来
N
行,每行一个整数Aᵢ
(1 ≤ Aᵢ ≤ 10⁵
),表示数列的每个元素。
输出描述
输出一个整数,表示 K 倍区间的数量。
输入样例
in
5 2
1
2
3
4
5
输出样例
out
6
c++代码
cpp
#include<bits/stdc++.h>
#include<stdio.h>
using namespace std;
typedef long long ll;
ll N, K, ans = 0;
unordered_map<int, int> mp;
vector<ll> arr;
int main() {
scanf("%lld %lld", &N, &K);
arr = vector<ll>(N + 1, 0);
for (ll i = 1; i <= N; i++) {
scanf("%lld", &arr[i]);
arr[i] += arr[i - 1];
}
if (K == 1) {
printf("%lld", (N * (N - 1)) / 2 + N);
return 0;
}
for (int i = 0; i <= N; i++) mp[arr[i] % K]++;
for (auto it = mp.begin(); it != mp.end(); it++) ans += ((it->second * (it->second - 1)) / 2);
printf("%lld", ans);
return 0;
}//by wqs
算法解析
首先前缀和想得到吧
cpp
for (ll i = 1; i <= N; i++) {
scanf("%lld", &arr[i]);
arr[i] += arr[i - 1];
}
现在有了前缀和数组
我们要找两个端点i, j,使得
cpp
(arr[j] - arr[i]) % k == 0
假设arr[j] % k = c, arr[i] % k = d,
根据取模公式(arr[j] - arr[i]) % k = c - d。
也就是我们要让c - d = 0,也就是c == d。
现在我们的任务转换为求有多少组i,j满足arr[i] % k == arr[j] % k。
cpp
for (int i = 0; i <= N; i++) mp[arr[i] % K]++;
for (auto it = mp.begin(); it != mp.end(); it++) ans += ((it->second * (it->second - 1)) / 2);//排列组合
printf("%lld", ans);