2. Problem
• 각 정점에 번호가 매겨진 트리 상의 임의의 경로를 구한
다. 경로상 번호들의 최장 증가 수열은?
• LIS 문제의 일반화 (트리에 한해서지만 뭐..)
• Naive는 O(n^4). 할 수 있나요?
• LIS를 인덱스 트리로 O(nlgn)에 구하는 방법을 아시나요?
3. O(n^2) / Tree DP 1
• (i,j) 경로를 잡고 그때그때 LIS를 구하는 방법은 소용이
없어 보인다.
• 루트가 있는 트리에서 경로의 성질은 무엇일까? 루트 방
향으로 올라갔다 다시 내려간다.
• 이것으로 상태를 표현해 보자.
4. O(n^2) / Tree DP 2
• 트리니까 경로를 i - LCA(i,j)와 LCA(i,j) - j로 분할할 수 있
다.
• 그렇다면.. 루트 기준으로 리프 쪽으로 dp를 돌린다음에
두 경로를 합치는 식으로 하면 되겠네?
• DFS를 통해서 전위순회 번호를 붙이고 모든 번호를 이걸
로 바꾸자. 이러면 정점 i의 하위 노드는 [i+1,i+size[i]-1]
임을 알수 있다.
• 이제 이를 통해서 dp 점화식을 짜보자.
5. O(n^2) / Tree DP 3
• 일반적인 LIS DP식과 매우 유사!
• dpl[i] = max(dpl[j] + 1) for (i < j < i + sz[i] && a[j] <
a[i])
• dpu[i] = max(dpu[j] + 1) for (i < j < i + sz[i] && a[j] >
a[i])
• Calculation of these : O(n^2)
6. O(n^2) / Tree DP 4
• 이제 경로 두개를 합치면 된다.
• 점 i를 경로의 LCA라 하고, 이 점을 무조건 지나는 경로를
생각해보자.
• 이때 dp값의 결과는 dpl[i] + dpu[i] - 1
• 하지만.. LCA 점이 LIS 상에 없다면?
• 하지만.. dpl[i]와 dpu[i]가 같은 서브트리에서 와서 경로
가 겹친다면..?
7. O(n^2) / Tree DP 5
• 하지만.. LCA 점이 LIS 상에 없다면?
• 이러한 경로를 따로 처리해주자.
• 두개의 Disjoint한 서브트리 i,j를 고른 후 dpl[i] + dpu[j]
값의 최대를 구하면 되지 않을까?
• i < j라 가정했을 경우 j >= i + size[i]. a[i]와 a[j]의 대소
에 따라서 dpl과 dpu는 유도리있게 처리하길 ^^;
• LIS w/o LCA -> O(n^2)
8. O(n^2) / Tree DP 6
• 하지만.. dpl[i]와 dpu[i]가 같은 서브트리에서 와서 경로
가 겹친다면..?
• 이 때문에 계산법을 바꿔야 한다. i 점을 잡은 후 그 점의
직계자손 (서브트리)를 보자.
• 각 서브트리에서 a[i]보다 작은 dpl[i]의 최댓값. a[i]보다
큰 dpu[i]의 최댓값들을 모두 계산한 후 저장한다.
• i당 선형 시간이니 여기까지 O(n^2)이다.
9. O(n^2) / Tree DP 7
• 이제 서브트리들을 모두 돌면서 dpl[i] + dpu[j]가 최대가 되는
쌍 i,j를 모두 열거한 후 최댓값을 가져간다. (i != j)
• 이는 O((점 i의 자식방향 에지수) ^ 2) 이다.
• Sum(i < n) (점 i의 자식 방향 에지수) = n - 1
• 모두 더하면 많아야 O(n^2). 아까와 같다.
• 이로써 모든 상태를 열거했다.
• Small AC! Large를 풀려면 차근차근 계산량을 줄여야 한다.
10. O(nlgn) 1
• 1. 입력 및 DFS 후 renumbering = O(n)
• 2. DP Table 생성 = O(n^2)
• 3. LCA가 경로에 없을 경우 = O(n^2)
• 4. LCA가 경로에 있을 경우 = O(n^2)
11. O(nlgn) 2
• dp[i] = (i < j < i + sz[i] & a[i] > a[j]) max(dp[j] + 1)
• 그냥 하면 스위핑이 안된다!
• 버킷으로 잘 안풀리더라 (ㅠㅠ)
• a[i] 기준으로 정렬한 후 a[i]가 큰 순서대로 처리.
12. O(nlgn) 3
• dp[i] = (i < j < i + sz[i] & a[i] > a[j]) max(dp[j] + 1)
• a[i]가 큰 순서대로 내려오면서 처리
• dp[i]를 그때그때 RMQ에 넣고 [i+1,i+sz[i]-1] 범위 내에
서 가장 큰거를 받아오고 이걸 반복. O(nlgn).
• vice versa.
• a[i]가 동일할 때에 대한 처리를 현명하게 ^^;
13. O(nlgn) 4
• 1. 입력 및 DFS 후 renumbering = O(n)
• 2. DP Table 생성 = O(nlgn)
• 3. LCA가 경로에 없을 경우 = O(n^2)
• 4. LCA가 경로에 있을 경우 = O(n^2)
14. O(nlgn) 5
• 0 <= i < n인 i에 대해 , a[j] > a[i] && j >= i + size[i] 인
dp[j] 중 최대를 찾은 후 dp[i]와 더한 후 최댓값을 갱신.
• 아까와 동일하게 RMQ를 만들어서 하면 됨.
• O(nlgn)
15. O(nlgn) 6
• 1. 입력 및 DFS 후 renumbering = O(n)
• 2. DP Table 생성 = O(nlgn)
• 3. LCA가 경로에 없을 경우 = O(nlgn)
• 4. LCA가 경로에 있을 경우 = O(n^2)
16. O(nlgn) 7
• i의 서브트리들을 돈다기 보다는 서브트리들을 한번에 미리 계산
해 놓은 후 나중에 i의 서브트리를 볼때 저장한 걸 보는게 현명함
• 서브트리 처리는 역시 아까와 동일! O(nlgn)
• pmax[i] = max(pmax[i-1],a[i]); 식으로 prefix max를 만들거나
• priority_queue 등 힙을 사용해서 i != j인 서브트리 쌍의 합의 최
댓값을 구하면 이는 O(n) ~ O(nlgn).
• 결국 O(nlgn)!
17. O(n^1.5)
• 이 문제는 버킷을 사용하면
O(n^1.5)에도 충분히 풀 수 있음.
• 공식 TC로 맥북에서 벤치마크
결과, O(nlgn) : 310ms.
O(n^1.5) : 2500ms
• 버킷 풀이로 시작했다가 개고생
함 ㅠㅠ
• 여러분 버킷 어셉 잘 안뜨고 시
간 존나 구려요. RMQ 짱짱맨
18. Thank You!
• 1. 입력 및 DFS 후 renumbering = O(n)
• 2. DP Table 생성 = O(nlgn)
• 3. LCA가 경로에 없을 경우 = O(nlgn)
• 4. LCA가 경로에 있을 경우 = O(nlgn)
• 총 시간 복잡도 = O(nlgn) (대략 5000byte)
• 수고하셨습니다!