문제 풀이

[백준] 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


결과)