본문 바로가기
PS/Baekjoon

[Baekjoon] 10986 - 나머지 합

by 서현 SEOHYEON 2023. 2. 15.

📝 문제

 

 

 주의

마지막 경우의 개수를 세는 변수도 long으로 선언해주어야 한다.

만약 10^6가지 수 모두 나머지가 같을때, 가능한 쌍의 개수는 (10^6) * (10^6 -1) / 2 이므로 int로 계산시 오버플로우 발생 

 

 

 

🔑 풀이 과정

이 문제는 약 2달전 코테 문제를 푼지 얼마 안됐을 때, 누적합 알고리즘을 배우면서 한 번 접했던 문제이다.

그 당시 모듈러 연산을 사용하라는 풀이를 보고도 이해가 가지 않고 해서 몇 번 시도하다가 넘어갔던 문제.

 

① 사용해야 할 알고리즘 확인

수의 개수(N)가 최대 10^6개, 주어진 시간 1초(약 10^8번 연산)이므로

이중 for문을 돌리면서 합을 구하는 것은 시간이 부족하다.

합 배열을 사용해서 누적합 알고리즘을 사용한다.

 

 

② 수의 범위를 체크

수의 개수(N)가 최대 10^6, 수의 범위가 최대 10^9 이므로

합의 최댓값은 10^15이므로 int를 넘어간다. 그러므로 합배열은 long을 사용해준다.

(앞으로는 오버플로우를 피하기 위해 되도록 int보단 long을 사용할 것)

 

 

③ 모듈러 연산의 분배법칙을 사용

(A-B) % M = ((A%M) - (B%M)) % M

우리가 찾는 것은 (Ai + ... + Aj) % M이 0이 되는 (i,j)쌍의 개수를 찾는것

이 식을 합 배열로 표현하면 (S[j] - S[i-1]) % M이 0이 되는 경우의 개수를 찾는 것

모듈러 연산의 분배법칙을 사용하여 이 식을 다시 풀면

((S[j] % M) - (S[i-1] % M)) % M 이 0이 되는 경우를 찾는 것

즉, (S[j] % M) == (S[i-1] % M) 인 경우를 찾는게 우리의 목표이다. ((S[j] % M), (S[i-1] % M)의 범위는 0이상 M미만이므로 뒤의 %M은 생략)

 

 

④ 나머지 배열을 생성

(S[i] % M) 의 값은 0이상 M미만의 값을 가진다.

나머지 배열을 생성하여 나머지가 0인경우 인덱스 0을 카운트, 1인경우 인덱스 1인 경우 카운트... 를 해준다.

예시 1로 표현하자면 다음과 같다. 

 

⑤ 가능한 쌍의 개수 계산

(1) 나머지가 0인 경우 = 입력된 수들의 1번째부터 i번째의 합이 M으로 나누어 떨어진다는 뜻

그러므로 나머지가 0인 경우의 수를 더해준다.

위 표현이 어렵다면, 이렇게 생각하기

위에서 우리가 구하는 것은
(S[j] % M) == (S[i-1] % M)
인 (i, j)쌍을 구하는 것이라 했다. (j는 i보다 크거나 같다.)

지금 이 경우는 i가 1이라서  (A1 + A2 + ... Aj)
(S[j] % M) == (S[i-1] % M)
→ (S[j] % M) == (S[0] % M)
→ (S[j] % M) == 0
인 경우인 것
===

위의 예시 1로 생각해보면
i=1, j=2
i=1, j=3
i=1, j=5
인 경우들

(2) 나머지 값이 같은 경우 중 2가지를 뽑는 경우

다만 이 경우, 뽑을 수 있는 경우가 2개 이상이어야 하므로 나머지 배열의 값이 2이상인지 체크.

순열 공식을 사용해 2가지를 뽑는 경우를 계산해서 더해준다.

nC2 = n! / (2! * (n-2)!)
→ n * (n-1) / 2

- 예시

위의 예시 1로 생각해보면


[나머지 값이 0인 경우]
3 * 2 / 2 = 3

i=3, j=3
A3 = 3 (3으로 나누어 떨어짐)
i=3, j=5
A3 + A4 + A5 = 3 + 1 + 2 = 6 (3으로 나누어 떨어짐)
i=4, j=5
A4 + A5 = 1 + 2 = 3 (3으로 나누어 떨어짐)

[나머지 값이 1인 경우]
2 * 1 / 2 = 1

i=2, j=4
A2 + A3 + A4 = 2 + 3 + 1 = 6 (3으로 나누어 떨어짐)

[나머지 값이 2인 경우]
없음

 

 

🔓 답안

import java.io.*;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        long[] s = new long[N+1];
        long[] mod = new long[M];

        st = new StringTokenizer(br.readLine());
        for(int i = 1; i <= N; i++){
            int num = Integer.parseInt(st.nextToken());
            s[i] = s[i-1] + num;
            mod[(int)(s[i]%M)]++;
        }

        long count = 0;
        count += mod[0];
        for(int i = 0; i < M; i++){
            if(mod[i] >= 2){
                count += (mod[i] * (mod[i]-1)) / 2;
            }
        }
        
        bw.write(count + "\n");
        bw.flush();
        bw.close();
    }

}

 

 

 

🖤 알고리즘 분류

- 수학

- 누적 합

'PS > Baekjoon' 카테고리의 다른 글

[Baekjoon] 11047 - 동전 0  (0) 2023.02.17
[Baekjoon] 2839 - 설탕 배달  (0) 2023.02.16
[Baekjoon] 1920 - 수 찾기  (0) 2023.02.14
[Baekjoon] 1260 - DFS와 BFS  (0) 2023.02.13
[Baekjoon] 2606 - 바이러스  (0) 2023.02.12

댓글