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 倍区间的数量。
输入样例
5 2
1
2
3
4
5
输出样例
6
c++代码
#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
算法解析
首先前缀和想得到吧
for (ll i = 1; i <= N; i++) {scanf("%lld", &arr[i]);arr[i] += arr[i - 1];
}
现在有了前缀和数组
我们要找两个端点i, j,使得
(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。
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);