给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 KK 倍区间的数目。
数据范围 ⭐ 10^5 -> O(nlogn) 或 O(n) ; long 类型 存数据
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6
⭐ 首先想一个暴力的做法(要求覆盖所有情况)
暴力:O(n^3) -> 前缀和:O(n^2)
① 枚举区间的右端点
② 枚举特定右端点的所有左端点
③ 前缀和优化求区间和(暴力枚举求和也行)
⭐ 对暴力解法进行优化
O(n logn) 或 O(n)
① 对求解公式进行化简,发现只要 前缀和 的余数 相等 就是 k倍区间
② 开一个余数数组,下标是 余数数值,存的是 该余数数值 在 0~R 中出现了多少次
import java.io.*;
import java.util.*;
public class K倍区间
{
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = (int) 1e5 + 10, n, k;
static long[] s = new long[N];// 前缀和
static long[] rm = new long[N];// 余数 remainder
public static void main(String[] args) throws IOException
{
String[] a1 = in.readLine().split(" ");
n = Integer.parseInt(a1[0]);
k = Integer.parseInt(a1[1]);
for (int i = 1; i <= n; i++)
{
long x = Long.parseLong(in.readLine());// 建议直接转换为 long 类型,以免造成精度损失
s[i] = s[i - 1] + x;
}
long res = 0;
//s[0] = 0; 所以rm[0] = 0,因为求对 前缀和 求同余的数时用到 s[r] - s[l-1] ,s[l-1] ,
//而 l-1 有可能是 0,所以得事先初始化 rm[0] 为 1
rm[0] = 1;
for (int i = 1; i <= n; i++)
{
res += rm[(int) s[i] % k]; // 数组的下标是 int 类型(强转)
rm[(int) s[i] % k]++;
}
System.out.println(res);
}
}