ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 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


    '문제 풀이' 카테고리의 다른 글

    [백준] 14620번 꽃길  (0) 2020.07.14
    [백준] 14923번 미로 탈출  (0) 2020.07.14
    [백준] 11048번 이동하기  (0) 2020.07.11
    [백준] 14925번 목장 건설하기  (0) 2020.07.11
    [백준] 10942번 팰린드롬  (0) 2020.07.10

    댓글

Designed by Tistory.