BOJ - 10986 나머지 합 해결 전략 (C++)

hyeok's Log·2022년 8월 5일
1

ProblemSolving

목록 보기
46/50
post-thumbnail

해결 전략

  본 문제는 컴퓨터공학과 학부 과정에서 '이산 수학'을 배운 이라면 반드시 한 번 이상은 들어봤을 '모듈러 연산 관련 공식'을 사용하면 해결할 수 있는 문제이다. 본인은 최초 풀이 시도에서 해당 공식을 떠올리지 못해 해결하지 못했었다.

  기본적으로 누적합을 이용하는 문제임은 어렵지 않게 알아차릴 수 있다. 문제는 그 사용 방법이다.

부분 구간의 합을 Area(i+1,j)라 하면, 'Area(i+1,j) = Psum(j) - Psum(i)'임은 자명하다.

부분 구간의 합이 M으로 나뉜다는 것은, (Psum(j) - Psum(i)) % M = 0임을 의미한다.

Modular 연산 공식에 의해, '(Psum(j) - Psum(i)) % M = 0'는
'Psum(j) % M = Psum(i) % M'로 변환
할 수 있다.

~> 그렇다. 본 문제는 Index에 대해 누적합을 구하되, 각 누적합의 Modular 연산 결과가 같은 녀석끼리 묶어, 그들로부터 경우의 수를 도출하면 되는 문제이다.


  문제의 예시를 토대로 이해해보자.

입력꼴 : 1 2 3 1 2
누적합 : 1 3 6 7 9
모듈라 : 1 0 0 1 0

~> 11/000의 두 묶음으로 분류할 수 있다. 각 묶음에서 단 방향 조합의 경우의 수를 뽑아내는 것은 단순한 연속합 공식을 통해 계산할 수 있다.

이때, 한 가지 더 주의할 점이 있다. M으로 Modular 연산한 결과 나머지가 0에 대해선, 이를 mod[0]이라 하면, 이 mod[0]은 최종 Count값에 더해주어야 한다.

M이 2이상부터이므로, 0묶음에 대해선, 하나 더 있다고 봐야한다. 즉, 위 예시에선 010010, 즉, 11/0000으로 봐야하는 것이다.

~> 엄밀한 예외처리가 요구되는, 상당히 난도가 있는 문제이다. 모듈라 공식은 자주 사용되는 공식이니 늘 기억하도록 하자.


  아래는 정답 코드이다.

#include <iostream>
// BOJ - 10986 Remainder Summation
using namespace std;

long long mod[1001];

int main(void) {
	int n, m, t;
	long long psum = 0, cnt = 0;
	ios_base::sync_with_stdio(false); cin.tie(NULL);

	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		cin >> t;
		psum += t;
		mod[psum % m]++;
	}

	for (int i = 0; i <= m; i++) 
		cnt += ((mod[i] * (mod[i] - 1)) / 2);

	cout << mod[0] + cnt;

	return 0;
}

0개의 댓글