문제 풀이

[백준] 13537번 수열과 쿼리1

컴영 2020. 7. 13. 18:39

13537 수열과 쿼리1


문제


길이가 N인 수열 A1, A2, ..., AN이 주어진다. 이때, 다음 쿼리를 수행하는 프로그램을 작성하시오

i j k: Ai, Ai+1, ..., Aj로 이루어진 부분 수열 중에서 k보다 큰 원소의 개수를 출력한다.


입력


첫째 줄에 수열의 크기 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)


출력


각각의 쿼리마다 정답을 한 줄에 하나씩 출력한다.



풀이)

수열의 길이도 10만까지 되며, 쿼리의 수도 10만까지 주어질 수 있다.

-> for문으로 일일히 하기에는 시간 초과가 남


세그먼트 트리를 이용해 각 구간마다 수열의 구간 원소들을 미리 저장해야 한다.


수열의 길이가 10일때 각 구간?(index 1~10까지 존재할 때)

1~10

1~5,          6~10

1~3, 4~5    6~8, 9~10

... 이런식


참고 그림)


풀이 방법

1. 세그먼트 트리 노드에 각 구간에 해당하는 수열의 원소들을 저장한다.

2. 트리의 각 노드의 구간 원소들을 오름차순으로 정렬한다.

3. 쿼리를 입력받은 후, 세그먼트 트리를 이용해 k보다 큰 수들의 개수를 구한다.

 3.1 원하는 쿼리 구간과 현재 살펴보고 있는 구간이 중복되지 않으면 큰 수의 개수 0

 3.2 원하는 쿼리 구간과 현재 살펴보고 있는 구간이 완전히 겹쳐서 중복된다면 현재 구간에서 큰 수의 개수 구함

-> upper_bound()사용

 3.3 원하는 쿼리 구간과 현재 살펴보고 있는 구간이 일부만 겹친다면 원하는 쿼리 구간에 포함되지 않는 정보 제거 위해 재탐색



참고

세그먼트 트리 : https://comyoung.tistory.com/10

upper_bound(): https://comyoung.tistory.com/207


코드)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <algorithm>    
#include <vector>
using namespace std;
 
int N,M;
int i, j, k;
vector<int> tree[400001];
 
//tree에 데이터 삽입하는 함수
//삽입할 값 위치, 삽입 값, 시작노드번호, 현재구간[left,right]
void update(int pos, int val, int node, int left, int right) {
    if (pos <left || pos>right) return;
    tree[node].push_back(val);
    int mid = (left + right) / 2;
    if (left != right) {//리프노드가 아닐 경우, node 자식들로 들어감
        update(pos, val, node * 2, left, mid);
        update(pos, val, node * 2 + 1, mid + 1, right);
    }
}
 
//쿼리 구하는 함수
//찾는 구간[L,R], 시작노드번호, k ,현재노드구간[left,right]
int query(int L, int R, int node, int k, int left, int right) {
    //1. 현재구간이 찾고자 하는 구간과 전혀 겹치지 않을 때 -> k보다 큰 수 개수 0
    if (R < left || right < L) return 0;
    //2. 현재구간이 찾고자 하는 구간에 포함
    if (L <= left && right <= R) return tree[node].end() - upper_bound(tree[node].begin(), tree[node].end(), k);
    //3. 현재구간이 찾고자 하는 구간에 일부 포함
    int mid = (left + right) / 2;
    return query(L, R, node * 2, k, left, mid) + query(L, R, node * 2 + 1, k, mid + 1, right);
}
 
int main()
{
    scanf("%d"&N);
    int temp = 0;
    for (int i = 1; i <= N; i++) {
        scanf("%d"&temp);
        update(i, temp, 11, N);//구간마다 값 삽입
    }
    for (int i = 0; i <= N * 4; i++)sort(tree[i].begin(), tree[i].end());//오름차순 정렬
    scanf("%d"&M);
    while (M--)
    {
        scanf("%d %d %d"&i, &j, &k);
        printf("%d\n", query(i, j, 1, k, 1, N));
    }
    return 0;
}
 
 
cs