ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준] 1395번 스위치
    문제 풀이 2020. 4. 11. 00:06

    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번 스위치까지 중 켜져 있는 상태의 스위치 개수를 묻는 일이다. 단, 초기에는 모든 스위치의 상태는 꺼져있는 상태로 되어있다.


    출력


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


    풀이)

    segment tree + lazy propagate 를 이용해서 푼 문제다.

    스위치가 100,000개 이므로, 계산하기 쉽게 넉넉히 4 * 100,000을 트리크기로 정해줬다.


    스위치가 켜진값을 1, 꺼진 값은 0 이라고 하고 반전시키는 계산은 1과 XOR시켜줬다.

    (1 XOR 1 = 0,     0 XOR 1 = 1)


    구간에 갱신이 일어난 후

    propagate함수에서 tree에 적용시켜줄 때

                                                                                                                              

    구간 범위에서 켜져있는 스위치 초기 개수가 n개일때, 반전시키면 켜져있는 스위치는 (구간 범위 - n)개임을 알아두자.

    [Left, Right]                                                                                          (Right - Left + 1 - n)


    (예시)

    범위가 2일때, 켜져있는 합이 0이면 -> 켜져야하는 스위치는  2 - 0 = 2 

                                   합이 1이면 -> 2 - 1 = 1

                                   합이 2이면 -> 2 - 2 = 0


    코드)

    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #include <stdio.h>
    #include <algorithm>
    using namespace std;
     
    int n, m;
    int tree[400000];//켜져있는 스위치 합 저장
    int lazy[400000];//lazy의 값이 1이면 -> 반전시켜야한다.
     
    void propagate(int node,int nodeL,int nodeR) {
        //nodeL부터 nodeR까지를 반전시킨다?
        //범위가 2일때, 켜져있는 합이 0이면 -> 켜져야 하는 스위치 2, 합이 1이면-> 1, 합이 2이면 0
        tree[node] = (nodeR - nodeL + 1- tree[node];
        if (nodeL != nodeR) {
            lazy[node * 2] ^= 1;
            lazy[node * 2 + 1] ^= 1;
        }
        lazy[node] = 0;
    }
     
    void update(int L,int R,int node,int nodeL,int nodeR) {
        if (lazy[node] != 0) propagate(node, nodeL, nodeR); // propagate먼저
        if (nodeR < L || R < nodeL)return;
        if (L <= nodeL && nodeR <= R) {//범위안에 든다면, 스위치 반전
            lazy[node] ^= 1;
            propagate(node, nodeL, nodeR);
            return;
        }
        int mid = (nodeL + nodeR) / 2;
        //자식들 업데이트
        update(L, R, node * 2, nodeL, mid);
        update(L, R, node * 2 + 1, mid+1, nodeR);
        //본인 새로 업데이트
        tree[node] = tree[node * 2+ tree[node * 2 + 1];
    }
     
    int query(int L,int R,int node,int nodeL,int nodeR) { //스위치 켜진 합
        if (lazy[node] != 0) propagate(node, nodeL, nodeR); // propagate먼저
        if (nodeR < L || R < nodeL)return 0;
        if (L <= nodeL && nodeR <= R) return tree[node];
        int mid = (nodeL + nodeR) / 2;
        return query(L, R, node * 2, nodeL, mid) + query(L, R, node*2+1, mid + 1, nodeR);
    }
     
     
    int main() {
        scanf("%d %d"&n, &m);
     
        while (m--)
        {
            int o, s, t;
            scanf("%d %d %d"&o, &s, &t);
            if (s > t)swap(s, t);
            if (o == 0) {
                update(s, t, 11, n);
            }
            else {
                printf("%d\n", query(s, t, 11, n));
            }
     
        }
        return 0;
    }
    cs


    결과)


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

    [백준] 2217번 로프  (0) 2020.04.12
    [백준] 13904번 과제  (0) 2020.04.11
    [백준] 9252번 LCS2  (0) 2020.04.10
    [백준] 1654번 랜선 자르기  (0) 2020.04.08
    [백준] 17836번 공주님을 구해라  (0) 2020.04.07

    댓글

Designed by Tistory.