第八届蓝桥杯省赛 K倍区间

问题描述
  给定一个长度为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

 
思路
   这题暴力复杂度为$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 }

 

 
 如有不当之处欢迎指出!
 
 
 
posted @ 2018-03-05 20:50  flyawayl  阅读(1201)  评论(0编辑  收藏  举报