포스트

BOJ 11833 돌 무게 재기

sorohue가 PS하는 블로그

BOJ 11833 돌 무게 재기

문제 링크입니다.

문제 요약

무게에 따른 오름차순으로 정렬된 $N$개의 돌이 주어집니다. 각 돌의 무게는 모두 서로 다르지만, 정확한 무게는 알 수 없습니다. 양팔저울에 돌을 올려놓는 쿼리가 누적해서 주어질 때, 각 상황마다 양팔저울이 어느 쪽으로 기울지 맞히거나, 알 수 없음을 밝히세요.

풀이

각 돌의 정확한 무게를 알 수 없기 때문에, 돌의 종류 별 개수를 통해 비교해야 합니다. 예를 들어 모든 종류의 돌이 왼쪽보다 오른쪽에 더 많으면 저울은 당연히 오른쪽으로 기울겠죠.

문제는 이렇게 하면 왼쪽에 1번 돌이 있고 오른쪽에 2번 돌이 있는 경우를 판정할 수 없게 됩니다. 그래서 정확히는, 한쪽의 돌을 다른 쪽의 더 무거운 돌과 매칭할 수 있는지 판정해야 합니다. 여기서 괄호 문자열 내지는 산 그림 같은 걸 떠올린 다음에 그에 맞는 적당한 세그를 떠올리면 문제를 풀 수 있습니다. 아래는 제가 2년 전에 풀었던 접근 방식입니다.

각 돌의 무게를 $W_1 , W_2 , \cdots , W_N$이라고 합시다. $N$번째 돌을 $N$개의 조각으로 분할합니다. 첫 번째 조각의 무게는 $W_1$, 두 번째 조각의 무게는 $W_2 - W_1$, … , $N$번째 조각의 무게는 $W_N - W_{N-1}$입니다. 돌의 무게가 모두 다르므로 이 값은 모두 양수입니다. 이를 각 돌마다 반복하면, 각 조각 별 개수를 비교해서 돌의 무게가 서로 다를 때에도 판정이 가능해집니다. 레이지 없이 처리할 수 있긴 한데 생각하기 귀찮으니까 그냥 레이지 세그로 구간에 1 또는 -1씩 뿌려주면서 최댓값/최솟값을 관리해 주면 문제를 해결할 수 있습니다.

총 시간 복잡도는 ${\cal O}(N \lg N)$입니다.

코드

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <bits/stdc++.h>
using namespace std;
int seg[2][456789], lazy[456789];
 
void prop(int now, int l, int r){
	if(lazy[now]){
		if(l != r){
			lazy[now<<1] += lazy[now];
			lazy[now<<1|1] += lazy[now];
		}
		seg[0][now] += lazy[now];
		seg[1][now] += lazy[now];
		lazy[now] = 0;
	}
}

void upd(int now, int l, int r, int L, int R, int k){
	prop(now, l, r);
	if(r < L || l > R) return;
	if(L <= l && r <= R){
		lazy[now] += k;
		prop(now, l, r);
		return;
	}
	int mid = (l + r)>>1;
	upd(now<<1, l, mid, L, R, k);
	upd(now<<1|1, mid+1, r, L, R, k);
	seg[0][now] = max(seg[0][now<<1], seg[0][now<<1|1]);
	seg[1][now] = min(seg[1][now<<1], seg[1][now<<1|1]);
}

int main(){
	cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);
	int n; cin >> n;
	for(int i = 0; i < n; i++){
		int r, s; cin >> r >> s;
		if(s == 1) upd(1, 1, n, 1, r, 1);
		else upd(1, 1, n, 1, r, -1);
		if(seg[1][1] >= 0) cout << ">\n";
		else if(seg[0][1] <= 0) cout << "<\n";
		else cout << "?\n";
	}
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.