评测地址:https://www.lanqiao.cn/problems/97/learning/
一、问题描述
给定一个长度为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
数据规模和约定
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
思路:
首先这道题很大可能会是一道数学题,只要找到相应的思路应该可以轻易上手。
其次题目又与区间求和有关所以很容易联想到前缀和所以我们使用sum[ ]存放前缀和。
我们设区间和sumi表示前 i 项的和则区间 [ i, j ]的和可以表示为sum[ j ] - sum[ i - 1 ],所以如果( sum[ j ] - sum[ i - 1 ] ) % k == 0那么就说明这个区间是题目中所说的 “ k倍区间 "。如果看到这里那么你离正确解法就只差捅破最后的窗户纸了!!
根据取余运算规则(a-b)%p=(a % p - b % p) % p,那么上面的判定条件就可以转换为 sum[i - 1] % k == sum[j] % k!!!发现了么?这说明只要满足两个前缀和取余后的余数相同就能组成一个 “ k倍区间 ”。举个例子:
序列:1、2 、3、4、5(假设 mod == 2)
前缀和:1、3、6、10、15
余数:1、1、0、0、1
这时候我们任选两个余数相同的即可组成一个 “ k倍区间 ”。
?这不就是排列组合的问题么,这时我们只要将余数使用cnt[]数组存放起来,计数之后就只剩下排列组合的问题了。
好了我们现在基本已经得到了这道题的思路了。
注意要用long long哦~
还有就是sum[]数组可以简化为sum变量。(自行体会)
(思路比代码长系列)
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 100005
typedef long long LL;
LL cnt[maxn];
LL n, k, num, ans = 0, sum = 0;
int main() {
cin >> n >> k;
memset(cnt, 0, sizeof(cnt));
cnt[0] = 1;
for (int i = 1; i <= n; i++) {
cin >> num;
sum += num;
cnt[sum % k]++;
}
for (int i = 0; i < k; i++)
ans += cnt[i] * (cnt[i] - 1) / 2;
cout << ans;
return 0;
}