이번 문제는 구간 내에서 특정 값보다 큰 원소의 개수를 효율적으로 계산하는 것이다. 배열의 길이와 쿼리의 수가 최대 100,000까지 가능하기 때문에, 효율적인 자료구조와 알고리즘이 필요했다. 이를 위해 머지 소트 트리(Merge Sort Tree)를 사용해 해결했다.
import java.io.*;
import java.util.*;
public class Main {
static int N, M, k;
static int[] A;
static List<Integer>[] segmentTree;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
// 입력 처리
N = Integer.parseInt(br.readLine());
A = new int[N + 1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= N; i++) {
A[i] = Integer.parseInt(st.nextToken());
}
M = Integer.parseInt(br.readLine());
// 머지 소트 트리 초기화
segmentTree = new ArrayList[4 * N];
for (int i = 0; i < 4 * N; i++) {
segmentTree[i] = new ArrayList<>();
}
build(1, N, 1);
// 쿼리 처리
StringBuilder sb = new StringBuilder();
for (int i = 0; i < M; i++) {
st = new StringTokenizer(br.readLine());
int l = Integer.parseInt(st.nextToken());
int r = Integer.parseInt(st.nextToken());
k = Integer.parseInt(st.nextToken());
int result = query(1, N, 1, l, r);
sb.append(result).append("\n");
}
// 결과 출력
System.out.print(sb.toString());
}
// 머지 소트 트리 빌드
static void build(int start, int end, int node) {
if (start == end) {
segmentTree[node].add(A[start]);
} else {
int mid = (start + end) / 2;
build(start, mid, node * 2);
build(mid + 1, end, node * 2 + 1);
segmentTree[node] = merge(segmentTree[node * 2], segmentTree[node * 2 + 1]);
}
}
// 두 리스트 병합
static List<Integer> merge(List<Integer> left, List<Integer> right) {
List<Integer> merged = new ArrayList<>();
int i = 0, j = 0;
while (i < left.size() && j < right.size()) {
if (left.get(i) <= right.get(j)) {
merged.add(left.get(i++));
} else {
merged.add(right.get(j++));
}
}
while (i < left.size()) merged.add(left.get(i++));
while (j < right.size()) merged.add(right.get(j++));
return merged;
}
// 쿼리 처리
static int query(int start, int end, int node, int left, int right) {
if (right < start || end < left) {
return 0;
}
if (left <= start && end <= right) {
return segmentTree[node].size() - upperBound(segmentTree[node], k);
}
int mid = (start + end) / 2;
return query(start, mid, node * 2, left, right) +
query(mid + 1, end, node * 2 + 1, left, right);
}
// upper_bound 구현
static int upperBound(List<Integer> list, int value) {
int low = 0, high = list.size();
while (low < high) {
int mid = (low + high) / 2;
if (list.get(mid) <= value) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
}
N = Integer.parseInt(br.readLine());
A = new int[N + 1];
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= N; i++) {
A[i] = Integer.parseInt(st.nextToken());
}
M = Integer.parseInt(br.readLine());
segmentTree = new ArrayList[4 * N];
for (int i = 0; i < 4 * N; i++) {
segmentTree[i] = new ArrayList<>();
}
static void build(int start, int end, int node) {
if (start == end) {
segmentTree[node].add(A[start]);
} else {
int mid = (start + end) / 2;
build(start, mid, node * 2);
build(mid + 1, end, node * 2 + 1);
segmentTree[node] = merge(segmentTree[node * 2], segmentTree[node * 2 + 1]);
}
}
static List<Integer> merge(List<Integer> left, List<Integer> right) {
List<Integer> merged = new ArrayList<>();
int i = 0, j = 0;
while (i < left.size() && j < right.size()) {
if (left.get(i) <= right.get(j)) {
merged.add(left.get(i++));
} else {
merged.add(right.get(j++));
}
}
while (i < left.size()) merged.add(left.get(i++));
while (j < right.size()) merged.add(right.get(j++));
return merged;
}
static int query(int start, int end, int node, int left, int right) {
if (right < start || end < left) {
return 0;
}
if (left <= start && end <= right) {
return segmentTree[node].size() - upperBound(segmentTree[node], k);
}
int mid = (start + end) / 2;
return query(start, mid, node * 2, left, right) +
query(mid + 1, end, node * 2 + 1, left, right);
}
static int upperBound(List<Integer> list, int value) {
int low = 0, high = list.size();
while (low < high) {
int mid = (low + high) / 2;
if (list.get(mid) <= value) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
머지 소트 트리를 사용해 구간 내 조건을 만족하는 원소 개수를 효율적으로 계산할 수 있었고, 최적화된 방식으로 모든 쿼리를 처리할 수 있었다.