본문 바로가기

알고리즘 공부 및 문제 풀이/백준(BOJ)

[boj] 백준 10986 나머지 합 (c++)

[문제]

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

 

10986번: 나머지 합

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

www.acmicpc.net

 

[풀이]

문제에서 생각해야 할 부분은 크게 2가지이다.

1. 인덱스 0부터 i 까지의 누적 합, 즉 sum[i]에 modulo 연산을 취했을 때 값이 0이라면, M으로 나누어 떨어지는 수이다. cnt[] 배열을 증가시켜줌으로써 개수를 구할 수 있다.

2.  특정 인덱스 i 부터 j 까지의 누적 합에 대해서도 M으로 나누어떨어지는지 확인이 필요하다.

이는 (sum[j] - sum[i]) % m = 0 (i<=j)인지 묻는 것이고,  sum[j] % m = sum[i] % m 과 같이 나타낼 수 있다. 

예를 들어 시작 인덱스가 1이고, 1, 4, 6, 7에 대해 m으로 나눈 나머지 값이 같다면, 이는 곧 1~4, 1~6, 1~7, 4~6, 4~7, 6~7 의 범위의 누적 합이 m으로 나누어 떨어진다는 뜻이다. 따라서 이에 대한 개수는 nC2로 알아낼 수 있다.

 

이 문제에서 또 한가지 주의할 점은 수의 범위이다. 처음, Ai<=10^9 이므로 sum은 long long으로 선언했지만 N이 최대 10^6이기에 cnt 배열은 int로 선언을 하였다. 그런데 cnt 배열도 long long으로 선언해야 통과가 되길래 의아했는데, cnt[i]*(cnt[i]-1) / 2 부분에서 계산 결과가 int 범위를 넘을 수 있다..! 따라서 애초에 cnt 배열을 long long으로 선언하거나  (long long) cnt[i]*(cnt[i]-1) / 2 과 같이 캐스팅을 해줘야 한다!

 

[코드]

 

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <stack>
#define INF 987654321

using namespace std;

int n, m;
long long sum;
int cnt[1001];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    int temp;
    for (int i = 0; i < n; i++) {
        cin >> temp;
        sum += temp;
        cnt[sum % m]++;
    }

    long long result = cnt[0];
    //nC2
    for(int i=0; i<m; i++){
        result += (long long)cnt[i]*(cnt[i]-1) / 2;
    }
    
    cout << result;

}