문제 링크
- https://icpc.me/25412
문제 출처
- 2022 CEOI Day2 2번
사용 알고리즘
- 세그먼트 트리
시간복잡도
- $O((N+M) \log (N+M))$
풀이
Subtask 1. $N \leq 2\,000, M \leq 10$ (10점)
모든 점들을 정렬합시다. $i$번째 사람과 $j(> i)$번째 사람은 최소한 $(j-i)D$ 이상 떨어져 있어야 합니다. 따라서 두 사람은 최대 $((j-i)D-(P_j-P_i))/2$ 만큼 이동해야 합니다. 그러므로 $((j-i)D-(P_j-P_i))/2$의 최댓값은 정답의 상한입니다.
$t$초 이후에는 두 사람의 거리가 최대 $2t$ 만큼 변할 수 있습니다. 따라서 정답 $T$는 모든 $i < j$에 대해 $P_j-P_i+2T \geq (j-i)D$를 만족해야 합니다. 식을 정리하면 $T \geq ((j-i)D-(P_j-P_i))/2$가 되므로 정답의 하한도 구했습니다.
상한과 하한이 같으므로 단순히 $((j-i)D-(P_j-P_i))/2$의 최댓값을 $O(M(N+M)^2)$ 시간에 구해서 출력하면 됩니다.
Subtask 2. $N \leq 200\,000, M \leq 10$ (24점)
함수 $f(i, j) = (j-i)D-(P_j-P_i)$를 정의합시다. $O(N^2)$개의 쌍을 모두 보지 않고 $f$의 최댓값을 구해야 합니다.
식을 정리하면 $f(i, j) = (jD-P_j) + (P_i-iD)$를 얻을 수 있고, 이때 $i$를 고정하면 $f(i, \ast)$의 최댓값은 suffix maximum을 이용해 구할 수 있습니다. 따라서 $O(N \log N + NM)$에 해결할 수 있습니다.
아래 코드는 매번 std::sort
를 호출하기 때문에 $O(NM \log N)$이지만, 새로 들어온 사람만 적절한 위치에 삽입하면 $O(N \log N + NM)$이 됩니다.
1 |
|
Subtask 3. $N = 0, M \leq 200\,000, b_i \leq b_{i+1}$ (59점)
맨 뒤에 새로운 값이 추가되기 때문에 suffix maximum을 전처리할 수 없습니다. 하지만 $j$를 고정한 다음 $f(\ast, j)$의 최댓값을 구하는 방식으로 접근하면 $P_i-iD$의 prefix maximum을 구하는 것이므로 동일한 방법으로 해결할 수 있습니다.
원소를 중간에 삽입하거나 정렬할 필요가 없기 때문에 시간 복잡도는 $O(M)$입니다.
1 |
|
Subtask 4. $N = 0, M \leq 200\,000$ (100점)
새로운 수가 추가되었을 때 함수 $f(i, j) = (j-i)D-(P_j-P_i)$가 어떻게 변화하는지 살펴봅시다.
$f(i, j) = (jD-P_j)+(P_i-iD) = (jD-P_j)-(iD-P_i)$의 최댓값을 구하는 것이 목표입니다. 어떤 위치 $x$에 수를 삽입하면 $x$보다 오른쪽에 있는 원소들의 $iD-P_i$ 값이 $D$씩 증가합니다.
기존에 있던 원소들 간의 $f$ 값은 이미 계산되어 있으므로, 새로 $x$에 삽입한 원소들에 의해 바뀐 $f$값, 즉 ($x$의 오른쪽에서의 최댓값) - ($x$의 왼쪽에서의 최솟값)을 구해서 정답에 반영해야 합니다.
$x$보다 오른쪽에 있는 원소들의 값을 $D$씩 증가시키는 연산, 구간의 최댓값과 최솟값을 찾는 연산은 좌표 압축을 수행한 뒤 세그먼트 트리를 이용해 처리할 수 있습니다.
전체 시간 복잡도는 $O((N+M) \log (N+M))$입니다.
전체 코드
1 |
|