Post

(백준) 2015번 - 수들의 합 4

2015번 - 수들의 합 4

(백준) 2015번 - 수들의 합 4

문제

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

풀이

S 를 누적 합 배열이라고 하자. S[i]A[0] 부터 A[i] 까지의 누적 합의 값이 저장되어 있다. 그렇다면 i부터 j까지의 부분 합은 S[j] - S[i - 1] 로 구할 수 있다.

다만, $N$의 최대값은 200,000이고, 시간 제한은 2초이므로, 이중 for문을 통해 부분 합을 구하게 되면 시간 초과가 발생한다(약 400억).

우리가 원하는 상황은 S[j] - S[i - 1] = K가 되는 상황이다. 식을 변형하면 S[i - 1] = S[j] - K가 된다. 이는 곧 현재 누적 합이 S[j]일 때, 이전의 누적 합이 S[j] - K인 경우를 찾아야 한다는 의미이다.

Pasted image 20251127142817.png

위 상황은 이전의 누적 합이 S[j] - K인 경우가 세 번 존재하므로, 구간 $[x, j] (0 \leq x \leq j)$ 에 존재하는 구간 합이 $K$인 경우는 세 번 존재한다.

이전의 누적 합의 등장 횟수를 해시 맵으로 관리한다. 주의해야 할 점은 S[j] - K가 0이 되는 상황이다. 이는 S[j] 자체가 $K$인 상황이며, 초기에 해시 맵에 누적 합이 0인 경우 등장 횟수가 1임을 설정해주어야 한다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <vector>
#include <map>

using namespace std;

int main() {
	int n, k;
	cin >> n >> k;
	vector<int> v(n);
	for (int i = 0; i < n; i++) {
		cin >> v[i];
	}

	int cumsum = 0;
	long long cnt = 0;
	map<int, int> m;
	m[0] = 1; // 자기 자신이 k인 경우

	for (int i = 0; i < n; i++) {
		cumsum += v[i];
		cnt += m[cumsum - k];
		m[cumsum]++;
	}

	cout << cnt;
}

주의해야 할 점은 cnt의 자료형이다. $N$은 최대 200,000이며, 이 때 만들어지는 부분 합의 수는 약 200억이다($N(N+1)/2$). 따라서 int대신 long long으로 선언해야 한다.

시간복잡도

for문에서 $O(N)$, map에서 $O(log(N))$이다. map은 내부적으로 레드-블랙 트리로 구현되어 있으므로, 삽입 및 조회 시의 시간복잡도가 $O(log(N))$이다.

따라서 전체 시간복잡도는 $O(Nlog(N))$이다.

공간복잡도

vector, map에서 각각 $O(N)$이므로, 전체 공간복잡도는 $O(N)$이다.

This post is licensed under CC BY 4.0 by the author.