https://www.acmicpc.net/problem/13537
&Title
13537번 - 수열과 쿼리 1
&Question
길이가 N인 수열 A1, A2, ..., AN이 주어진다.
이때, 다음 쿼리를 수행하는 프로그램을 작성하시오.i j k: Ai,
Ai+1, ..., Aj로 이루어진 부분 수열 중에서 k보다 큰
원소의 개수를 출력한다.
&Input
첫째 줄에 수열의 크기 N (1 ≤ N
≤ 100,000)이 주어진다.둘째 줄에는 A1, A2, ..., AN이 주어진다.
(1 ≤ Ai ≤ 109)셋째 줄에는 쿼리의 개수 M
(1 ≤ M ≤ 100,000)이 주어진다.넷째 줄부터 M개의 줄에는
쿼리 i, j, k가 한 줄에 하나씩 주어진다. (1
≤ i ≤ j ≤ N, 1 ≤ k
≤ 109)
&Output
각각의 쿼리마다 정답을 한 줄에 하나씩 출력한다.
&Example
-input
5
5 1 2 3 4
3
2 4 1
4 4 4
1 5 2
-output
2
0
3
요약
i - j 범위에서 k보다 큰 원소의 개수를 구하여 출력하라.
접근
처음으로 풀어보는 수열과 쿼리 시리즈이다. 당연히 푸는법을 몰라서 갓휘님의 풀이를 보고 풀었다.(갓휘만세!) 세그트리만 배웠다면 쉽게 이해할 수 있다.
일단 숫자의 범위가 1e9이기 때문에 숫자의 크기로 합트리를 만들수가 없다 ㅜㅜ
그렇기 때문에 다른 방법을 떠올려야 되는데 바로 mergesorttree이다..
세그트리를 안다면 금방 알 수 있는데, 노드가 i~j범위의 노드인 경우 i~j의 수를 다 정렬한 상태로 갖고 있는거다.
그럴 경우 i-j범위에서 k보다 큰 숫자의 위치만 이진탐색으로 찾는다면 그보다 큰 위치는 확정적으로 k보다 클것이다.
즉 범위를 나누어 k보다 큰 수를 찾는 방식이다!!
시간복잡도는 O(nlogn+mlog^3n)이다
다운탑방식인 풀이도 있지만 변태 같아서 패스~
풀이
먼저 업데이트 방식을 보자 target이 start-end범위를 속하는 숫자인 경우 원소를 추가해준다.
void update(int index, int target, int value, int start, int end) {
if (target < start || target > end)
return;
tree[index].push_back(value);
if (start != end) {
int mid = (start + end) / 2;
update(index * 2, target, value, start, mid);
update(index * 2 + 1, target, value, mid + 1, end);
}
}
쿼리 부분이다. tree[index].end() - upper_bound(tree[index].begin(), tree[index].end(), value); 이 부분이 머지소트트리를 이용해 k보다 큰 수를 찾는 것의 핵심이다. i-j로 범위가 좁혀지기 때문에 해당 범위들로 미리 정렬된것을 이용해 이진탐색으로 k보다 큰 수의 수를 구한다.
int query(int index, int value, int left, int right, int start, int end) {
if (left > end || right < start)
return 0;
if (left <= start && end <= right)
return tree[index].end() - upper_bound(tree[index].begin(), tree[index].end(), value);
else {
int mid = (start + end) / 2;
return query(index * 2, value, left, right, start, mid) +
query(index * 2 + 1, value, left, right, mid + 1, end);
}
}
댓글남기기