백준 26481: 수열과 쿼리 42, 시간 초과가 난 LIS 풀이
백준 26481번 ‘수열과 쿼리 42’를 풀다가 시간 초과가 났습니다. 아직 해결하지 못한 코드이므로 정답 풀이로 참고하면 안 됩니다.
쿼리마다 LIS를 다시 구합니다
각 쿼리의 양 끝 l, r을 0부터 시작하는 인덱스로 바꾸고 그 구간의 최장 증가 부분 수열(LIS) 길이를 계산합니다.
lis에는 길이별로 증가 부분 수열의 마지막 값 후보를 저장합니다. 새 값이 현재 마지막 값보다 크면 길이를 늘립니다. 그렇지 않으면 이분 탐색으로 새 값 이상인 첫 위치를 찾아 교체합니다. lis 배열 전체가 실제 부분 수열을 나타내는 것은 아닙니다.
쿼리가 올 때마다 새 배열을 만들어 해당 구간을 처음부터 끝까지 다시 확인합니다. 구간 길이를 L이라 하면 쿼리 하나에 O(L log L)이 듭니다. 모든 쿼리가 전체 수열을 다루는 경우에는 O(QN log N)의 계산이 필요합니다. 둘 다 실제 실행 시간을 측정하지 않고 코드 구조만 보고 따진 비용입니다.
시간 초과가 난 코드
import java.io.*;
import java.util.*;
class Main {
static int N,Q;
static int l,r;
static int[] arr;
static int[] lis;
static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine()," ");
N = Integer.parseInt(st.nextToken());
Q = Integer.parseInt(st.nextToken());
arr = new int[N];
st = new StringTokenizer(br.readLine(), " ");
for (int i = 0; i < N; i++) {
arr[i] = Integer.parseInt(st.nextToken());
}
for (int i = 0; i < Q; i++) {
st = new StringTokenizer(br.readLine(), " ");
l = Integer.parseInt(st.nextToken())-1;
r = Integer.parseInt(st.nextToken())-1;
int longest = longest();
bw.write(String.valueOf(longest));
bw.write("\n");
}
bw.flush();
}
public static int longest() throws IOException {
lis = new int[r-l+1];
int j = 0;
if ((r - l + 1) == N) {
lis[0] = arr[0];
} else {
lis[0] = arr[l];
}
for(int i =l ; i < r+1; i++) {
if (lis[j] < arr[i]) {
lis[j + 1] = arr[i];
j += 1;
}
else {
int idx = binarySearch(0, j, arr[i]);
lis[idx] = arr[i];
}
}
return j + 1;
}
public static int binarySearch(int left, int right, int target) {
int mid;
while (left < right) {
mid = (left + right) / 2;
if (lis[mid] < target) {
left = mid + 1;
}
else {
right = mid;
}
}
return right;
}
}