본문 바로가기

Problem Solving/Baekjoon

[백준 10986 C++] 나머지 합

백준 10986번 나머지 합 문제 링크

https://www.acmicpc.net/problem/10986

 

10986번: 나머지 합

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

www.acmicpc.net

 

문제 풀이

백준 10986번 나머지 합 문제는 누적합을 이용한 문제로 [i, j] 구간 합이 M 으로 나누어 떨어지는 경우의 수를 세는 문제입니다. 해당 문제를 모든 구간 합을 구해서 M으로 나눌 경우, 시간 초과가 발생하게 됩니다. 그 이유는 N 값이 10^6 이기 때문에 O(N^2) 방법으로는 문제를 해결할 수가 없습니다

 

이 문제의 핵심은 구간 합의 나머지가 같은 것들끼리 그룹을 형성한 후, 나머지가 같은 두 개의 구간 합을 서로 빼주게 되면 나머지가 0이 되면서 구간 합이 M으로 나누어 지는 경우를 쉽게 찾을 수 있게 됩니다. 

 

예를 들어, [1 ... j ] 라는 구간이 있을 때, sum(1... j) - sum(1 ... i)  (i <= j) 연산을 하게 되면 sum(i + 1 ... j)의 구간 합이 나오게 됩니다. 이와 같이 두 구간 합의 차이는 특정 구간의 합이 되는 것을 알 수 있습니다. 

 

나아가, 구간 합을 M으로 나눈 나머지 값이 동일한 그룹에서 2개를 선택하여 연속된 구간의 합 ( B - A ) 을 M 으로 나눌 경우 나머지가 0이 됩니다. 

 

즉, 나머지 값이 동일한 구간 합들 중 2개를 선택한 경우를 모두 더하게 되면 구간 합에서 M으로 나누어지는 경우의 수를 구할 수 있게 되는 것 입니다.

 

위 방식으로 문제를 해결하게 될 경우, O(N + M) 만에 문제를 해결할 수 있습니다. 

 

소스 코드

#include <bits/stdc++.h>
#define SIZE 1000010
using namespace std;

typedef unsigned long long ull ;
typedef long long ll; 
ull A[SIZE] ;
ull S ; 
int N, M ;
ll cnt ; 
int main() {
    ios::sync_with_stdio(0); cin.tie(0) ; 

    cin >> N >> M ;
    for (int i = 0, k; i < N ; i++) { 
        cin >> k; 
        S = (S + k % M) % M ; 
        A[S]++;
    }

    A[0]++;

    for (int i = 0; i < M ; i++) {
        if ( A[i] < 2 ) continue ;
        cnt += A[i] * (A[i] - 1) / 2;
    }
    cout << cnt << '\n';
    return 0;
}

'Problem Solving > Baekjoon' 카테고리의 다른 글

[백준 17780 C++] 새로운 게임  (0) 2023.07.20
[백준 10021 C++] Watering the Fields  (0) 2023.07.18
[백준 1208 C++] 부분 수열의 합 2  (0) 2022.01.05
[백준 2638 C++] 치즈  (0) 2022.01.03
[백준 2407 C++] 조합  (0) 2021.12.30