알고리즘/백준

[Java | 세그먼트 트리| 1395번 | 플3] 스위치

이진지니지니진 2025. 2. 27. 10:47

☑️ 문제

https://www.acmicpc.net/problem/1395

준규네 집에는 총 N개의 스위치가 있고 이를 편하게 1번부터 N번까지 차례대로 번호를 매겼다. 그리고 준규의 취미는 이 스위치들을 켜고 끄는 것이다.

준규가 하는 스위치를 갖고 노는 일은 크게 두 가지이다. 하나는 A번부터 B번 사이의 스위치 상태를 반전시키는 것이고 다른 하나는 C번부터 D번 사이의 스위치 중 켜져 있는 상태의 스위치의 개수를 세는 것이다.

하지만 준규가 싫증을 느껴 우리가 이 귀찮은 일을 떠맡게 되었고 프로그래밍을 통해 일을 처리하도록 결정하였다.

 

입력

첫 줄에는 스위치의 개수 N(2 ≤ N ≤ 100,000)과 처리할 일의 개수 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 M개의 줄에 대해 각 줄에 처리할 일에 대한 정보가 담겨진 세 개의 정수 O, Si, Ti가 입력된다. O가 0이면 Si번 스위치부터 Ti번 스위치까지 스위치 상태를 반전시키는 일이고 1이면 Si번 스위치부터 Ti번 스위치까지 중 켜져 있는 상태의 스위치 개수를 묻는 일이다. 단, 초기에는 모든 스위치의 상태는 꺼져있는 상태로 되어있다.

 

출력

입력에서 켜진 스위치 개수에 대한 답을 한 줄에 하나씩 출력한다.

 

예제 입력1

4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4

 

예제 출력1

1
2

 

☄️ 정답코드

import java.io.*;
import java.util.*;

public class Main {
    static int N, M;
    static int[] tree, lazy;

    static void update(int node, int start, int end, int left, int right) {
        propagate(node, start, end);

        if (left > end || right < start) return;
        if (left <= start && end <= right) {
            lazy[node] ^= 1;
            propagate(node, start, end);
            return;
        }

        int mid = (start + end) / 2;
        update(node * 2, start, mid, left, right);
        update(node * 2 + 1, mid + 1, end, left, right);
        tree[node] = tree[node * 2] + tree[node * 2 + 1];
    }

    static void propagate(int node, int start, int end) {
        if (lazy[node] == 1) {
            tree[node] = (end - start + 1) - tree[node];
            if (start != end) {
                lazy[node * 2] ^= 1;
                lazy[node * 2 + 1] ^= 1;
            }
            lazy[node] = 0;
        }
    }

    static int query(int node, int start, int end, int left, int right) {
        propagate(node, start, end);

        if (left > end || right < start) return 0;
        if (left <= start && end <= right) return tree[node];

        int mid = (start + end) / 2;
        int lsum = query(node * 2, start, mid, left, right);
        int rsum = query(node * 2 + 1, mid + 1, end, left, right);
        return lsum + rsum;
    }

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

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

        tree = new int[4 * N];
        lazy = new int[4 * N];

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int type = Integer.parseInt(st.nextToken());
            int a = Integer.parseInt(st.nextToken()) - 1;
            int b = Integer.parseInt(st.nextToken()) - 1;

            if (type == 0) {
                update(1, 0, N - 1, a, b);
            } else {
                sb.append(query(1, 0, N - 1, a, b)).append("\n");
            }
        }

        System.out.print(sb);
    }
}

🪐 풀이

세그먼트 트리 with 느리게 갱신

https://book.acmicpc.net/ds/segment-tree-lazy-propagation

여기를 참고했는데, 느리게 갱신되는 세그먼트 트리가 뭔지 모르 참고!!!!

 

update나 query를 실행하는 방식은 기존의 세그먼트 트리 기본 문제들과 같기 때문에,

propagate함수에 대해서만 설명하겠다!

 

✅ propagate

lazy[node]가 1이라면, 반영해야 할 게 있다는 의미라, 값을 반영해 줍니다. 

static void propagate(int node, int start, int end) {
    if (lazy[node] == 1) {
        tree[node] = (end - start + 1) - tree[node];
        
        // 리프노드가 아니라면
        if (start != end) {
            lazy[node * 2] ^= 1;		// ^는 XOR 연산자 
            lazy[node * 2 + 1] ^= 1;		// 같으면 0, 다르면 1 반환
        }
        
        lazy[node] = 0;
    }
}