오늘 풀어본 문제는 ⭐숫자 카드2 라는 문제이다.
N (1 ≤ N ≤ 500,000)M이 방식은 최악의 경우 시간 초과가 발생할 수 있다.
이분 탐색 자체는 O(log N)이지만,
N/2N/2까지 탐색해야 한다.
즉, 한 번의 질의에 최대 O(N)이 걸릴 수 있다.
만약 카드의 개수가 최대치라면?
O(N)O(N × M)N, M이 최대 500,000일 경우
500,000 × 500,000 = 250,000,000,000
사실상 O(N²) 수준이 되어 시간 초과가 발생한다.
정렬된 배열에서 특정 값 x의 개수는 다음처럼 구할 수 있다.
lower_bound(x) : x가 처음 등장하는 위치upper_bound(x) : x보다 큰 값이 처음 등장하는 위치따라서 x의 등장 횟수는
count(x) = upper_bound(x) - lower_bound(x)

이분탐색 upper bound / lower bound 찾기
둘 다 구조가 동일하다. 하지만 lower의 경우, 찾는 범위가 =에 포함되어야 하고, upper은 포함되지 않는다는 것이 다르다.
[lower bound]
: lower의 경우, 현재 타겟과 같은 값을 찾았다면 그건 정답일 가능성이 있다.
=> 그렇기에 인덱스를 옮기지 않고 hi만 내린다.
int lo = 0;
int hi = N;
while( lo < hi ) {
int mid = (lo + hi) / 2;
if ( value [mid] <= target )
hi = mid;
else
lo = mid + 1;
}
return lo;
[upper bound]
: upper의 경우, 현재 값이 찾는 값과 같다면, 범위에서 벗어나는 것이다.
(upper은 찾는 수가 처음으로 초과되는 인덱스를 찾는다.)
=> 그렇기에 같은 경우는 밑의 케이스은 mid + 1을 통해 lo를 올린다.
int lo = 0;
int hi = N;
while( lo < hi ) {
int mid = (lo + hi) / 2;
if ( value [mid] < target )
hi = mid;
else
lo = mid + 1;
}
return lo;


import java.io.*;
import java.util.*;
public class Main {
static int N;
static int[] number;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
N = Integer.parseInt(br.readLine());
number = new int[N];
st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {
number[i] = Integer.parseInt(st.nextToken());
}
Arrays.sort(number);
int M = Integer.parseInt(br.readLine());
st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < M; i++) {
int x = Integer.parseInt(st.nextToken());
sb.append(upperBound(x) - lowerBound(x));
if (i < M - 1) {
sb.append(" ");
}
}
System.out.println(sb.toString());
}
static int lowerBound(int target) {
int lo = 0;
int hi = N;
while (lo < hi) {
int mid = (hi + lo) / 2;
if (number[mid] >= target) {
hi = mid;
} else {
lo = mid + 1;
}
}
return lo;
}
static int upperBound(int target) {
int lo = 0;
int hi = N;
while (lo < hi) {
int mid = (hi + lo) / 2;
if (number[mid] > target) {
hi = mid;
} else {
lo = mid + 1;
}
}
return lo;
}
}