第八届蓝桥杯省赛 K倍区间
问题描述
给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
你能求出数列中总共有多少个K倍区间吗?
输入格式
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
输出一个整数,代表K倍区间的数目。
样例输入
5 2
1
2
3
4
5
1
2
3
4
5
样例输出
6
数据规模和约定
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
CPU消耗 < 2000ms
思路
这题暴力复杂度为$O(n^2)$,无法通过所有数据。我提供两种思路来解决这个问题:
首先求出数组的前缀和并对k取余。前缀和是数组的前j个元素的和$\sum\limits_{i = 1}^j {a[i]}$。假设经过求余得到的数组为$\{ {y_1},{y_2},...,{y_n}\}$,比如样例中的$y$数组为{1,1,0,0,1}。
1.对于数组$y$,如果${y_i} = {y_j}(i < j)$,那么$\sum\limits_{t = i + 1}^j {{t_i}}$必定是$k$的倍数。设$c_x$表示数组$y$等于$x$的元素个数,但是要特别注意等于0的情况,要让$c_0+1$!!
答案就是$\sum\limits_{i = 0}^{k - 1} {{c_i} \times ({c_i} - 1)/2}$。
2.在计算数组$y$同时,第$i$个元素能和前面的元素构成$c_{y_i}-1$个k倍区间,这样一直计算下去就是答案。
总而言之,这两种思路都是基于余数相同区间和必为k的倍数来实现的。
AC代码
1 #include <stdio.h> 2 #include <string.h> 3 const int maxn = 100000 + 5; 4 typedef long long LL; 5 int a[maxn], cnt[maxn]; 6 int n, k; 7 int main() { 8 while(scanf("%d%d", &n, &k) == 2) { 9 int sum = 0; 10 LL ans = 0; 11 memset(cnt, 0, sizeof(cnt)); 12 for(int i = 0; i < n; i++) { 13 scanf("%d", &a[i]); 14 sum = (sum + a[i] % k) % k; 15 cnt[sum]++; 16 } 17 cnt[0]++; 18 for(int i = 0; i < k; i++) { 19 ans += (1LL * cnt[i] * (cnt[i]-1)) / 2; 20 } 21 printf("%lld\n", ans); 22 } 23 return 0; 24 }
如有不当之处欢迎指出!