본문 바로가기

백준

C++[백준]9345번 디지털 비디오 디스크(DVDs)

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

 

9345번: 디지털 비디오 디스크(DVDs)

손님이 DVD를 카운터에 가져왔을 때 손님이 원하는 DVD가 전부 존재하면, (A번 선반부터 B번 선반까지에 있는 DVD를 전부 가져왔을 때 순서에 상관없이 A번 DVD부터 B번 DVD까지 있다면) "YES"를 출력하

www.acmicpc.net

 

솔루션


일반적인 구간 세그먼트 트리를 생각헀다가 해법을 떠올리기 힘들어서 꽤나 고생했다..

결국은 세그먼트트리를 두개를 이용해서 문제를 해결 할 수 있는 아이디어를 얻었다.

한개는 구간에서의 최대값을 저장하는 세그먼트트리, 다른 한개는 구간의 최소값을 저장하는 세그면트 트리를 이용하였다.

 

1) 각 배열에 arr[i] = i 로 값을 채워주고 min, max 세그먼트 트리를 만든다.

2) Q와 a,b를 입력받고 아래의 과정을 걸친다.

 2-1) Q가 0이면, swap(arr[a],arr[b])를 해 주고, 세그먼트 트리를 갱신한다.

 2-1) Q가 1이면, 구간의 최대 최소값을 구하고 최소값과 a가 같고, b와 최대값이 같으면 YES를 출력하고 아니면 No를 출력한다.

 

최대 최소 값으로 어떻게 답을 도출 해 낼 수 있는지 예를 들어 설명하면 세가지 경우로 볼 수 있다.

 

특정 구간 l,r이 있을 때 a,b를 바꿀 때,

 

1) (l<= a <=b <= r) 인 경우

 구간 내에서 바뀌기 때문에  구간에서의 최대 최소가 변하지 않는다.

 따라서 손님은 DVD를 무사히 빌려 갈 수 있다.

 

2) (a< l <= b <= r) 인 경우

 a가 l보다 작은 값에서 바뀌었기 때문에  구간 l, r 사이에 최소 값이 변하게 된다.

 따라서 DVD를 빌릴 수 없다.

 

 

3) (l<=a<=r<b) 인 경우

 b가 r보다 크므로 구간 l, r 사이에 최대값이 변하게 되어 DVD를 빌릴 수 없다.

 

min-max 세그먼트 트리를 이렇게 변형해서 문제를 만드는 게 너무 신기하다..

 

코드


#define pii pair<int,int>
#define INF 987654321
#include <iostream>
using namespace std;

int minseg[400002];
int maxseg[400002];
int arr[100002];
int N,M;

void update(int node){
	maxseg[node] = max(maxseg[node * 2], maxseg[node * 2 + 1]);
	minseg[node] = min(minseg[node * 2], minseg[node * 2 + 1]);
	return;
}

void makeSeg(int str, int end, int node) {
	if (str == end) {
		minseg[node] = arr[str];
		maxseg[node] = arr[str];
		return;
	}
	else {
		int mid = (str + end) / 2;
		makeSeg(str, mid, node * 2);
		makeSeg(mid + 1, end, node * 2 + 1);
		update(node);
		return;
	}
}

void UpdateSeg(int str, int end, int node, int idx) {
	if (str == end) {
		minseg[node] = arr[str];
		maxseg[node] = arr[str];
	}
	else {
		int mid = (str + end) / 2;
		
		if (idx <= mid) {
			UpdateSeg(str, mid, node * 2, idx);
		}
		else {
			UpdateSeg(mid + 1, end, node * 2 + 1, idx);
		}
		update(node);
	}
	return;
}

pii getRange(int str, int end, int l, int r, int node) { //{큰 값, 작은 값}
	if (str > r || end < l) return { -INF,INF };
	if (str >= l && end <= r) return { maxseg[node],minseg[node] };
	int mid = (str + end) / 2;
	pii tmp1 = getRange(str, mid, l, r, node * 2);
	pii tmp2 = getRange(mid + 1, end, l, r, node * 2 + 1);
	return { max(tmp1.first,tmp2.first),min(tmp1.second,tmp2.second) };
}

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);

	int tc;
	cin >> tc;
	while (tc--) {
		cin >> N >> M;
		for (int i = 0; i < N; i++) {
			arr[i] = i;
		}
		makeSeg(0, N - 1, 1);


		for (int i = 0; i < M; i++) {
			int Q, a, b;
			cin >> Q >> a >> b;
			//a--; b--;
			if (Q == 0) {//선반 바꿔끼우기.
				swap(arr[a], arr[b]);
				UpdateSeg(0, N - 1, 1, a);
				UpdateSeg(0, N - 1, 1, b);
			}
			else { //책을 빌리기
				pii ans = getRange(0, N - 1, a, b, 1);
				
				if (ans.first == b && ans.second == a) {
					cout << "YES\n";
				}
				else cout << "NO\n";
			}
		}
		
	}
}

'백준' 카테고리의 다른 글

C++[백준]2243번 사탕상자  (0) 2023.04.22
C++[백준]2244번 민코프스 합  (0) 2023.04.20
C++[백준]3653번 영화 수집  (0) 2023.04.19
C++[백준]3006번 터보소트  (0) 2023.04.18
C++[백준]12873번 기념품  (0) 2023.04.18