1. 문제
1부터 n까지 적힌 카드가 아래에서부터 위로 순서대로 쌓여있다.
이제 다음과 같은 질문이 주어진다.
x : 카드 뭉치에서 x번이 적힌 카드를 찾아 맨 아래로 내린다.
-x : 카드 뭉치에서 x번이 적힌 카드를 찾아 맨 위로 올린다.
예를 들어 n = 5라고 하자.
카드가 [1,2,3,4,5]로 쌓여있다고 표현할 수 있고 [3,-2,1,-4,-5] 순서대로 질문이 주어진다.
3이 주어지면, [1,2,3,4,5]에서 3을 찾아 맨 아래로 내리면 [3,1,2,4,5]
-2가 주어지면 [3,1,4,5,2]
1이 주어지면 [1,3,4,5,2]
-4가 주어지면 [1,3,5,2,4]
-5가 주어지면 [1,3,2,4,5]
최종 상태는 [1,3,2,4,5]가 된다.
n개의 카드가 순서대로 쌓여있는 상태에서 q개의 질문이 주어질때, 모든 질문이 끝나고 난 뒤의 최종 상태를 출력하는 프로그램을 작성하라
2. 제한
1≤n≤500000, 1≤q≤500000, 1≤x≤n
이전에 3이라는 query가 주어지면, -3이라는 query가 주어질 수 있으며, 중복해서 주어질 수도 있다.
3. 풀이
사실 가장 쉬운 방법?은 매 순간 배열에서 x를 찾아서 x를 제거하고 x > 0 이면 맨 앞에 삽입하고
x < 0 이면 맨 뒤에 삽입하는건데
당연히 x를 찾는 과정이 O(N)이며 제거하는 것도 O(N)일거고 삽입하는것도 맨 뒤에 삽입하면 O(1)이겠지만 맨 앞에 삽입하면 O(N)이다..
N,Q가 500000이면 최악의 경우 평생해도 안끝나겠는데
나는 스택을 이용해서 풀긴했지만.. 로직이야 깔끔해도 O(N2)인건 마찬가지라...
4. 온라인 쿼리와 오프라인 쿼리
온라인 쿼리는, 쿼리를 받을때마다 그 때 그 때 쿼리에 답을 하는 방법이고
오프라인 쿼리는, 그 때 그 때 쿼리에 답을 하지 않고,
어떤 식으로든지 내가 유리한 방향대로 쿼리를 재배치해서 답을 하는 테크닉이다.
핵심은 쿼리가 주어짐과 동시에 답을 해야한다는 생각부터 버려야 할때가 있다는 것
쿼리를 미리 다 받아놓고, 거꾸로 쿼리를 답하든지, 어떤 식으로든 다른 방법으로도 답을 할 수 있다는 테크닉을 익히는게 중요
5. 오프라인 쿼리를 이용한 풀이
쿼리가 주어질때마다 배열을 재배치하지 말고, 일단 모든 쿼리를 다 받아보자.
n = 10이고 q = 7인데 [3,-2,4,-1,-9,2,7]이라고 해보자.
[1,2,3,4,5,6,7,8,9,10]에서 3을 찾아서 [3,1,2,4,5,6,7,8,9,10]이라고 생각하지 말고..
그냥 배열이 비어있는 상태에서 [3,-2,4,-1,-9,2,7]을 각각 처리하면 어떻게 되는지 생각해보자
3을 받으면, [3,0,0,0,0,0,0,0,0,0]
-2를 받으면, [3,0,0,0,0,0,0,0,0,2]
4를 받으면, [4,3,0,0,0,0,0,0,0,2]
-1을 받으면, [4,3,0,0,0,0,0,0,2,1]
-9를 받으면, [4,3,0,0,0,0,0,2,1,9]
2를 받으면, [2,4,3,0,0,0,0,0,1,9]
7을 받으면, [7,2,4,3,0,0,0,0,1,9]
그리고 나머지 비어있는 0,0,0,0에는 그동안 안넣은 5,6,8,10을 차례대로 넣으면 될 것 같다
[7,2,4,3,5,6,8,10,1,9]
실제로 [1,2,3,4,5,6,7,8,9,10]에서 [3,-2,4,-1,-9,2,7]을 수행한다고 생각해보자. 정말로 저렇게 되나?
3을 받으면 [3,1,2,4,5,6,7,8,9,10]
-2를 받으면 [3,1,4,5,6,7,8,9,10,2]
4를 받으면