K倍区间
题目来源:第八届蓝桥杯省赛Java/C++大学B组
时间限制:
1000
m
s
1000ms
1000ms 内存限制:
64
m
b
64mb
64mb
题目描述
给定一个长度为
N
N
N 的数列,
A
1
,
A
2
,
…
,
A
N
A_1,A_2,…,A_N
A1,A2,…,AN,如果其中一段连续的子序列
A
i
,
A
i
+
1
,
…
,
A
j
A_i,A_{i+1},…,A_j
Ai,Ai+1,…,Aj 之和是
K
K
K 的倍数,我们就称这个区间
[
i
,
j
]
[i,j]
[i,j] 是
K
K
K 倍区间。
你能求出数列中总共有多少个
K
K
K 倍区间吗?
输入格式
第一行包含两个整数
N
N
N 和
K
K
K 。
以下
N
N
N 行每行包含一个整数
A
i
A_i
Ai 。
输出格式
输出一个整数,代表 K K K 倍区间的数目。
数据范围
1
≤
N
,
K
≤
100000
1 ≤ N,K ≤ 100000
1≤N,K≤100000 ,
1
≤
A
i
≤
100000
1 ≤ Ai ≤ 100000
1≤Ai≤100000
样例输入
5 2
1
2
3
4
5
样例输出
6
解题思路
一个经典的前缀和题目
在读入
A
i
A_i
Ai 的时候直接存入前
i
i
i 项和。
求区间
[
i
,
j
]
[i,j]
[i,j] 的和的时候,直接用前
j
j
j 项和
S
j
S_j
Sj 减去前
i
i
i 项和
S
i
S_i
Si 就得到了,然后对其模
K
K
K,判断是否为
0
0
0 即可。
直接做会超时。
优化:
如果
(
S
i
−
S
j
)
%
k
=
0
(S_i - S_j) \%k = 0
(Si−Sj)%k=0 ,那么
S
i
%
k
S_i\%k
Si%k 和
S
j
%
k
S_j\%k
Sj%k 都等于
0
0
0 。
问题就转化为了,枚举终点坐标
x
x
x ,求有多少个数模
k
k
k的余数是
S
x
%
k
S_x\%k
Sx%k。即查找某一个数出现了多少次。
开一个
c
n
t
cnt
cnt 数组,
c
n
t
[
x
]
cnt[x]
cnt[x] 表示,余数为
x
x
x 的数的个数。
即求的就是
c
n
t
[
S
x
%
k
]
cnt[S_x\%k]
cnt[Sx%k] ,每次算完就让
c
n
t
[
S
x
%
k
]
+
+
cnt[S_x\%k]++
cnt[Sx%k]++ 。
解题代码-Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int k = input.nextInt();
long[] a = new long[n + 1];
for (int i = 1; i <= n; i++) {
a[i] = a[i - 1] + input.nextInt();
}
input.close();
long ans = 0;
int[] cnt = new int[n+1];
cnt[0]++;
for (int i = 1; i <= n; i++) {
ans += cnt[(int) (a[i]%k)];
cnt[(int) (a[i]%k)]++;
}
System.out.println(ans);
}
}