1. 문제
10986번: 나머지 합
수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오. 즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j)
www.acmicpc.net
2. 풀이
누적합이야 prefix sum 방법으로 O(N)에 미리 구해놓고 [i,j]의 구간합은 O(1)에 구할 수 있는데
정수 M으로 나누어 떨어지는 구간합 [i,j]를 어떻게 하면 아주 빠르게 구할 수 있을까
가장 쉬운 방법은 모든 i,j에 대해 검사하는 이중 for문 방법이지만
N이 106이다 보니 O(N2)으로는 1초안에 통과할 수 있을리가 없다
그런데 [i,j]의 구간합을 어떻게 구하는지 생각을 해보면..
배열 A에서 0번 원소부터 i번 원소까지의 누적합 배열 prefix를 미리 O(N)에 구하고,
그러면 [i,j]의 구간합은 prefix[j] - prefix[i-1]로 O(1)에 구할 수 있다
그러니까 목표는 prefix[j] - prefix[i-1]이 정수 M으로 나누어 떨어지는 그러한 (i,j)의 개수를 구해야한다
-----------------------------------------------------------------------------------------------------------------
그런데 나머지를 구하는 모듈로 연산의 성질을 생각해보면,
https://deepdata.tistory.com/399
정수론 - 합과 곱은 왜 계속 나눠도 문제가 없는가? -
1. 문제 https://www.acmicpc.net/problem/1904 1904번: 01타일 지원이에게 2진 수열을 가르쳐 주기 위해, 지원이 아버지는 그에게 타일들을 선물해주셨다. 그리고 이 각각의 타일들은 0 또는 1이 쓰여 있는 낱
deepdata.tistory.com
prefix[j]−prefix[i−1](modM)≡(prefix[j](modM)−prefix[i−1](modM))(modM)
prefix[j]와 prefix[i-1] 각각을 M으로 나눈 나머지끼리 뺀 값을 M으로 나눈 나머지가
prefix[j]-prefix[i-1]을 M으로 나눈 나머지와 같다는 사실을 알고 있다.
이전에 O(N)으로 모든 prefix[i]를 이미 구해놨다.
그렇다면 모든 prefix[i]에 대해 M으로 나눈 나머지를 구한 다음에, 나머지가 서로 같은 값끼리 그룹을 지은다면 어떨까?
M으로 나눈 나머지는 0~M-1까지 가능하므로 크기 M인 배열에 각 그룹을 저장해두자.
그리고 각 그룹에서 2개를 뽑아 A,B라고 한다면,
A,B는 M으로 나눈 나머지가 서로 동일하다.
따라서 A와 B를 빼준 값은 M으로 나눈 나머지는 0이 된다.
그리고 A와 B를 빼준 값이 어떤 구간 [i,j]인지 특정할 수는 없지만, 어떤 구간 [i,j]의 구간합이 되는 것은 확실하다.
이 방법을 이용한다면 i,j를 모두 순회해보지 않더라도 M으로 나눈 나머지가 0인 구간합을 O(M)만에 구할 수 있게 된다.
import math
from sys import stdin
n,m = map(int,stdin.readline().split())
A = list(map(int,stdin.readline().split()))
prefix = [0]*(n+1)
group = [[] for _ in range(m)]
group[0].append(0)
for i in range(1,n+1):
#prefix sum을 구해준다.
prefix[i] = prefix[i-1] + A[i-1]
#prefix sum 각각을 m으로 나눈 나머지를 구하고 각 그룹 내에 prefix sum값을 저장한다.
group[prefix[i]%m].append(prefix[i])
answer = 0
#m으로 나눈 나머지가 동일한 그룹에서 2개를 뽑는 방법의 수를 전부 더하면,
#모든 경우에 [i,j]의 구간합이 m으로 나눈 나머지가 0인 경우가 된다.
for j in range(m):
x = len(group[j])
answer += math.comb(x,2)
print(answer)
또 한번 지렸다,.. 진짜 아이디어가 경이롭다
'알고리즘 > 누적 합 알고리즘' 카테고리의 다른 글
누적합 응용 - prefix/suffix min/max 배열 이용한 테크닉 (0) | 2024.02.12 |
---|---|
거짓말하는 사람 수 알아내는 방법(놀라운 양방향 누적합 테크닉) (0) | 2024.02.05 |
2차원 배열에서의 누적합 배열을 구하는 방법 배우기 (0) | 2023.11.14 |
수열의 구간 합을 빠르게 계산하는 방법1 -접두사 합(prefix sum)- (0) | 2022.10.30 |
이차원 배열끼리 덧셈은 어떻게 효율적으로 할 수 있을까 (0) | 2022.04.19 |