알고리즘

[백준] 11505번 구간 곱 구하기 _ JAVA ( 주석 설명 ) 본문

백준 - JAVA/세그먼트 트리

[백준] 11505번 구간 곱 구하기 _ JAVA ( 주석 설명 )

wch_s 2023. 4. 5. 15:10

풀이

'부모노드 = 왼쪽 자식노드 * 오른쪽 자식노드' 이다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
    static int[] ary;
    static long[] tree;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        int K = Integer.parseInt(st.nextToken());

        ary = new int[N+1];

        //트리의 높이 구하기
        int tree_height = (int) Math.ceil(Math.log(N)/Math.log(2));
        //2^(트리 높이+1)
        int tree_size = 1<<tree_height+1;
        tree = new long[tree_size];

        //리프노드일 경우 지점에 맞는 ary의 값을 가져와야 한다.
        for(int i=1;i<=N;++i){
            ary[i] = Integer.parseInt(br.readLine());
        }

        init(1, 1, N);
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<M+K;++i){
            st = new StringTokenizer(br.readLine());

            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            int c = Integer.parseInt(st.nextToken());

            //b번째 수를 c로 바꾸기
            if(a==1){
                update(1,1, N, b, c);
            }

            //b부터 c까지의 곱 구하기
            if(a==2){
                long result = query(1, 1, N, b, c);
                sb.append(result).append('\n');
            }
        }

        System.out.println(sb);
    }



    //리프노드가 나올 때까지 재귀 호출을 시킨다.
    //나오면, 쌓인 재귀를 풀어가면서 부모~최상위노드까지 세그먼트 트리를 만든다.
    private static long init(int node, int start, int end) {
    	//*리프노드일 경우 배열의 그 수를 저장해야 한다.
        if(start==end)
            return tree[node] = ary[start];

        int mid = (start+end)/2;

		//*구간곱을 tree[node]에 저장해야 한다.
        return tree[node] = init(node*2,start, mid) * init(node*2+1,mid+1, end) % 1000000007;
    }

    private static long query(int node, int start, int end, int left, int right) {
        //범위 바깥이면
        //*&&가 아닌 ||이다.
        if(right<start || left>end)
            return 1;

        //구간 범위에 있으면
        if(left<=start && right>=end)
            return tree[node];

        int mid = (start+end)/2;

        long lval = query(node*2, start, mid, left, right);
        long rval = query(node*2+1, mid+1, end, left, right);

        //start, end 구간을 원하는 left, right에 맞춰가면서 구간곱 구하기
        return lval*rval % 1000000007;
    }

    private static void update(int node, int start, int end, int index, int change) {
        //*index가 구간을 벗어난 경우
        //바꿀 건 없다.
        if(index<start || index>end)
            return;

        //*리프노드를 찾을 때까지 계속 재귀 호출을 시킨다.
        //*찾으면 ary의 해당 인덱스 값과 그와 매칭되는 tree[node(리프노드)]를 변경해주고
        //*쌓여있던 재귀를 풀면서 부모노드~최상위노드(1)까지 차례대로 세그먼트 트리를 재구축한다.
        if(start==end) {
            ary[index] = change;
            tree[node] = change;
            return;
        }

        int mid = (start+end)/2;

        update(node*2, start, mid, index, change);
        update(node*2+1, mid+1, end, index, change);

        //*세그먼트 트리 재구축
        tree[node] = tree[node*2] * tree[node*2+1] % 1000000007;
    }
}