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

2023-05-23,,

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

 #include <stdio.h>
#include <string.h>
const int maxn = + ;
typedef long long LL;
int a[maxn], cnt[maxn];
int n, k;
int main() {
while(scanf("%d%d", &n, &k) == ) {
int sum = ;
LL ans = ;
memset(cnt, , sizeof(cnt));
for(int i = ; i < n; i++) {
scanf("%d", &a[i]);
sum = (sum + a[i] % k) % k;
cnt[sum]++;
}
cnt[]++;
for(int i = ; i < k; i++) {
ans += (1LL * cnt[i] * (cnt[i]-)) / ;
}
printf("%lld\n", ans);
}
return ;
}

 
 如有不当之处欢迎指出!
 
 
 

第八届蓝桥杯省赛 K倍区间的相关教程结束。

《第八届蓝桥杯省赛 K倍区间.doc》

下载本文的Word格式文档,以方便收藏与打印。