새소식

PS/백준

[백준/3653] 영화 수집 (Python)

  • -

Problem : https://www.acmicpc.net/problem/3653

 

3653번: 영화 수집

각 테스트 케이스에 대해서 한 줄에 m개의 정수를 출력해야 한다. i번째 출력하는 수는 i번째로 영화를 볼 때 그 영화의 위에 있었던 DVD의 개수이다. 상근이는 매번 영화를 볼 때마다 본 영화 DVD

www.acmicpc.net

 

Difficulty : Platinum 4

 

Status : Solved

 

Time : 01:24:23

 


 

문제 설명

 

더보기

 

상근이는 영화 DVD 수집가이다. 상근이는 그의 DVD 콜렉션을 쌓아 보관한다.

보고 싶은 영화가 있을 때는, DVD의 위치를 찾은 다음 쌓아놓은 콜렉션이 무너지지 않게 조심스럽게 DVD를 뺀다. 영화를 다 본 이후에는 가장 위에 놓는다.

상근이는 DVD가 매우 많기 때문에, 영화의 위치를 찾는데 시간이 너무 오래 걸린다. 각 DVD의 위치는, 찾으려는 DVD의 위에 있는 영화의 개수만 알면 쉽게 구할 수 있다. 각 영화는 DVD 표지에 붙어있는 숫자로 쉽게 구별할 수 있다.

각 영화의 위치를 기록하는 프로그램을 작성하시오. 상근이가 영화를 한 편 볼 때마다 그 DVD의 위에 몇 개의 DVD가 있었는지를 구해야 한다.

 

 

입력 및 출력

 

더보기

입력

 

첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스의 개수는 100개를 넘지 않는다.

각 테스트 케이스의 첫째 줄에는 상근이가 가지고 있는 영화의 수 n과 보려고 하는 영화의 수 m이 주어진다. (1 ≤ n, m ≤ 100,000) 둘째 줄에는 보려고 하는 영화의 번호가 순서대로 주어진다.

영화의 번호는 1부터 n까지 이며, 가장 처음에 영화가 쌓여진 순서는 1부터 증가하는 순서이다. 가장 위에 있는 영화의 번호는 1이다. 

 

출력

 

각 테스트 케이스에 대해서 한 줄에 m개의 정수를 출력해야 한다. i번째 출력하는 수는 i번째로 영화를 볼 때 그 영화의 위에 있었던 DVD의 개수이다. 상근이는 매번 영화를 볼 때마다 본 영화 DVD를 가장 위에 놓는다.

 

입력 예시

 

2 3 3 3 1 1 5 3 4 4 5

 

출력 예시

 

2 1 0 3 0 4

 

 


 

풀이

 

이 문제가 요구하는 것은

 

1. idx에 대한 조회 쿼리

2. idx간의 위치 변경에 대한 쿼리

 

둘 모두가 최소 시간 복잡도 내로 이루어져야 한다는 점이다. 여기서 search, update 모두 O(logN)의 시간복잡도를 가지는 세그먼트 트리를 생각해 볼 수 있겠다.

 

세그먼트 트리는 다음과 같이 동작한다.

 

  • 초기화 : 세그먼트 트리의 각 리프 노드는 그 우선도를 가지는 노드를 의미한다. 또한, 각 DVD마다 현재 어떤 우선도를 가지는지를 동시에 기록해, 현재 인덱스의 DVD가 가지는 우선도를 O(1)시간복잡도 내에 구할 수 있도록 한다. 또한, 쿼리 특성상 최대 우선도는 N+M개가 존재할 수 있다. i번째 DVD의 우선도는 N-i이 된다.
  • 업데이트 : 우선 타겟된 DVD의 우선도를 업데이트할 필요가 있다. 현재 DVD의 우선도를 가리키는 리프 노드를 0으로 업데이트하고, 이 타겟된 DVD의 우선도가 제일 크도록 다시 업데이트해야 한다. 즉 (현재 트리에서 제일 큰 우선도) + 1로 업데이트된다. 시간복잡도는 O(log(N+M))이 소요된다.
  • 검색 : 타겟된 DVD가 위에서 어디에 위치했는지 검색하려면, 이 DVD보다 우선도가 높은 모든 노드를 카운팅하면 된다. 즉 분할 정복 문제이므로, O(log(N+M))의 시간복잡도 내에 해결할 수 있다.

 

풀이 코드

import sys input = sys.stdin.readline class SegTree : def __init__(self, N, M) : self.size = N + M self.tree = [0]*(4*(N+M)) self.loc = [-1]*N self.idx = N def _init(start, end, idx) : if start == end : if start < N : self.tree[idx] = 1 self.loc[N-start-1] = start return mid = (start + end) // 2 _init(start, mid, idx*2) _init(mid+1, end, idx*2+1) self.tree[idx] = self.tree[idx*2] + self.tree[idx*2+1] _init(0, self.size-1, 1) def update(self, target) : def _update(start, end, idx, target, val) : if end < target or start > target : return if start == end == target : self.tree[idx] = val return mid = (start + end) // 2 _update(start, mid, idx*2, target, val) _update(mid+1, end, idx*2+1, target, val) self.tree[idx] = self.tree[idx*2] + self.tree[idx*2+1] return target_idx = self.loc[target] _update(0, self.size-1, 1, target_idx, 0) _update(0, self.size-1, 1, self.idx, 1) self.loc[target] = self.idx self.idx += 1 def search(self, target): target_idx = self.loc[target] def _search(start, end, idx) : if end < target_idx : return 0 if target_idx <= start : return self.tree[idx] mid = (start + end) // 2 return _search(start, mid, idx*2) + _search(mid+1, end, idx*2+1) print(_search(0, self.size-1, 1)-1, end = ' ') def solve() : N, M = map(int, input().split()) segtree = SegTree(N, M) for idx in map(int, input().split()) : segtree.search(idx-1) segtree.update(idx-1) print() for _ in range(int(input())) : solve()

풀이 완료!

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.