ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 10986번 나머지 합
    문제 풀이 2020. 8. 1. 13:56

    10986 나머지 합


    문제


    수 N개 A1, A2, ..., AN이 주어진다. 이때, 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 구하는 프로그램을 작성하시오.

    즉, Ai + ... + Aj (i ≤ j) 의 합이 M으로 나누어 떨어지는 (i, j) 쌍의 개수를 구해야 한다.


    입력


    첫째 줄에 N과 M이 주어진다. (1 ≤ N ≤ 106, 2 ≤ M ≤ 103)

    둘째 줄에 N개의 수 A1, A2, ..., AN이 주어진다. (0 ≤ Ai ≤ 109)


    출력


    첫째 줄에 연속된 부분 구간의 합이 M으로 나누어 떨어지는 구간의 개수를 출력한다.



    풀이)

    update 쿼리가 없고, 구간 합만 구해서 나머지를 확인하면 되는 문제라

    segment tree 가 아니고 prefix sum 을 이용한 문제다.

    그리고 모든 구간을 직접 prefix sum을 이용해 나머지를 확인하면 시간 초과가 나는 문제여서

    모듈러 성질을 생각해서 다른 방법을 사용해야 한다.


    prefix sum 성질

    a[1] + a[2] + ... + a[i] = sum[i];

    a[L] + a[L + 1] + a[L +2]+ ... + a[R] = sum[R] - sum[L-1];


    모듈러 성질

    (A + B) % C = (A % C + B % C) % C


    따라서,

    [L,R] 구간합의 나머지가 0인지는 (sum[R] - sum[L-1]) % M = 0


    (sum[R] % M - sum[L-1] % M) % M = 0


    누적합에서 나머지가 같은 것끼리 조합하면, 그것이 M으로 나누어 떨어지는 구간의 개수

    sum[0] ~ sum[N]을 M으로 나눈 나머지가 각각 몇개씩 있는지 계산한 뒤, 그것을 이용한다.


    문제 예시) 

    N=5, M=3

    {1 2 3 1 2} 의 경우, sum[] = { 1 3 6 7 9 } 나머지는 {1 0 0 1 0}

    3으로 나눈 나머지가 0인 것은 3개, 1인 것은 2개, 2인 것은 0개

    3 + 3 combination 2 + 2 combination 2가 정답 

    (나머지가 0인 구간은 2개 조합안하고도 M으로 나누어 떨어지므로 개수에 더해줘야 한다.)


    구간 (구간이 1부터 시작해서 5까지 있다고 할 때)

    나머지 0 => 3개                       (1,2) / (1,3) / (1,5)

    나머지 0 조합 =>3*2/2 = 3개     (1,3-1,2) = (3,3) / (1,5-1,2) = (3,5) / (1,5 - 1,3) = (4,5)

    나머지 1 조합 =>2*1/2 = 1개     (1,4-1,1) = (2,4)


    코드)


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <stdio.h>
    using namespace std;
     
    int N, M; // N <= 1000000
    long long arr[1000001];
    long long cnt[1001];
     
    int main() {
        scanf("%d %d"&N, &M);
        for (int i = 1; i <= N; i++) {
            int temp;
            scanf("%d"&temp);
            arr[i] = (arr[i - 1+ temp) % M;
            cnt[arr[i]]++;
        }
        long long result = cnt[0]; //구간 자체가 m으로 나눠지는 
        for (int i = 0; i < M; i++) {
            result += (cnt[i] * (cnt[i] - 1)) / 2;//나머지가 같은 횟수끼리 2개 조합
        }
        printf("%lld", result);
     
        return 0;
    }
    cs


    '문제 풀이' 카테고리의 다른 글

    [백준] 2942번 퍼거슨과 사과  (0) 2020.08.03
    [백준] 1202번 보석 도둑  (0) 2020.08.02
    [백준] 17780번 새로운 게임  (0) 2020.07.31
    [백준] 6087번 레이저 통신  (0) 2020.07.30
    [백준] 9328번 열쇠  (0) 2020.07.29

    댓글

Designed by Tistory.