[蓝桥杯 2017 省 B] k 倍区间
题目描述
给定一个长度为 的数列,,如果其中一段连续的子序列 之和是 的倍数,我们就称这个区间 是 倍区间。
你能求出数列中总共有多少个 倍区间吗?
输入格式
第一行包含两个整数 和 K$$(1 \le N,K \le 10^5)。
以下 行每行包含一个整数 A_i$$(1 \le A_i \le 10^5)。
输出格式
输出一个整数,代表 倍区间的数目。
样例 #1
样例输入 #1
5 2
1
2
3
4
5
样例输出 #1
6
提示
时限 2 秒, 256M。蓝桥杯 2017 年第八届
思路
首先定义一些全局变量,包括数列的长度 ,倍数 ,数列 ,前缀和数组 ,以及一个映射 mcnt 来存储前缀和模 的结果的计数。
在 main 函数中,首先初始化 ps[0] 为 0,mcnt[0] 为 1。初始化 mcnt[0] 为 1,就相当于在所有的前缀和中预先加入了一个和为 0 的前缀和,这样就能正确计算出那些直接就是 倍数的子序列数量。
然后读取 和 的值,接着读取数列 的各个元素,并计算其前缀和,同时更新 mcnt 映射。
计算前缀和的目的是为了快速得到任意一个子序列的和,而 mcnt 映射的目的是为了快速找到前缀和模 结果相同的子序列。由同余定理可知,如果两个前缀和模 的结果相同,那么这两个前缀和对应的子序列之间的序列和必定是 的倍数。
接着遍历 mcnt 映射,对于映射中的每一个值,如果其计数大于 1,那么就说明存在多个前缀和模 的结果相同,也就是存在多个和为 的倍数的连续子序列。这些子序列可以任意两两组合,所以可以用组合公式 来计算子序列的数量,其中 是前缀和模 结果相同的数量。
最后输出计算得到的和为 的倍数的连续子序列的总数量。
注意
要开 long long
AC代码
#include <algorithm>
#include <iostream>
#include <map>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int n, k;
int a[N];
ll ps[N];
map<int, ll> mcnt;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ps[0] = 0;
mcnt[0] = 1;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ps[i] = ps[i - 1] + a[i];
mcnt[ps[i] % k]++;
}
ll ans = 0;
for (const auto m : mcnt) {
// cout << m.first << " " << m.second << "\n";
if (m.second > 1) {
ll s = m.second;
ans += (s * (s - 1)) >> 1;
}
}
cout << ans << "\n";
return 0;
}