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

 

수열이 a1, a2, ..., an 으로 주어졌을때 1 ~ ai 범위의 숫자를 적절하게 골라서 1 ~ N의 수가 하나씩 등장하도록 만들어야 합니다.

 

5
3 4 3 2 5

즉 이런 수열이 주어졌을 때

a1은 1 ~ 3 중에서 2

a2는 1 ~ 4 중에서 4

a3은 1 ~ 3 중에서 3

a4는 1 ~ 2 중에서 1

a5는 1 ~ 5 중에서 5

이렇게 골라서 1 ~ N까지 하나씩 등장하도록 순열을 만들 수 있습니다.

그리고 수열을 M번 수정하여 각각 수정된 수열에서도 위처럼 1 ~ N을 만들 수 있는지 구하는 문제입니다.

 

이 문제에서 저는 Segment Tree Lazy를 사용했고, 트리에는 해당 숫자를 몇 번 사용할 수 있는지 카운팅을 해줍니다.

1 ~ 5가 있다면

1은 1번, 2는 2번, 3은 3번, 4는 4번, 5는 5번 사용할 수 있습니다.

5 5 5 5 5가 입력된다면 5에서 수를 5번 고른다는 뜻입니다.

만약 4 4 4 4 4가 입력된다면 5개의 4에서 1 ~ 5를 하나씩 만들 수가 없게 되는거죠.

그렇게 i ~ N을 +1씩 업데이트를 해주고, 각각 노드에서의 최솟값으로 갱신하도록 합니다.

 

그 다음에는 최초로 주어진 수열에서 1 ~ N을 만들 수 있는지 먼저 확인합니다.

list[i]를 사용한게 되기 때문에 list[i] ~ N의 범위에 -1로 업데이트를 시켜줍니다.

트리를 최솟값으로 저장한 이유는 이 연산 과정에서 해당 값의 카운트가 음수인 경우를 빠르게 캐치하기 위해서입니다.

수열의 모든 수를 대상으로 업데이트가 끝난다면 query 함수를 이용하여 트리의 최솟값을 가져옵니다.

최솟값이 0 이상이면 순열을 만들 수 있고, 음수면 만들 수 없습니다.

 

다음 할 작업은 idx번째 수를 x로 변경하는 것입니다.

처음에는 위에서 -1로 업데이트를 한걸 되돌려야 하는지 고민했었는데 결과적으로 시간초과가 뜨더라고요!

N은 20만, M은 50만이기 때문에 시간초과가 당연한거였습니다.

20만개의 수를 50만번 업데이트 해야되니까요.. ㅎ

 

좀더 고민했을때는 트리에는 수열에 대한 카운팅이 각각 진행된 상태였고,

어차피 업데이트되는 idx번째 수에 대해서만 카운팅을 바꿔주면 된다는 결론이 나왔습니다.

그래서 수를 업데이트 하기 전에 기존 list[idx]의 범위에 대한 업데이트를 +1로 진행해주고,

새로운 값인 x의 범위에 대한 업데이트를 -1으로 진행해주면 새로운 수열에서의 카운팅 작업이 완료되는 것입니다.

마지막에는 업데이트된 트리의 최솟값을 가져와서 0 이상이면 `TAK`을, 음수면 `NIE`를 출력해주시면 되겠습니다.

 

 

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <algorithm>
#define MAX 200001
using namespace std;
 
int list[MAX];
int tree[MAX << 2];
int lazy[MAX << 2];
int N, M;
 
void lazyUpdate(int node, int l, int r) {
    if (!lazy[node]) return;
    tree[node] += lazy[node];
    if (l != r) {
        lazy[node << 1+= lazy[node];
        lazy[(node << 1+ 1+= lazy[node];
    }
    lazy[node] = 0;
}
 
int update(int node, int l, int r, int s, int e, int diff) {
    lazyUpdate(node, l, r);
    if (l > e || s > r) return tree[node];
    if (s <= l && r <= e) {
        lazy[node] += diff;
        lazyUpdate(node, l, r);
        return tree[node];
    }
 
    int m = (l + r) >> 1;
    return tree[node] = min(update(node << 1, l, m, s, e, diff), update((node << 1+ 1, m + 1, r, s, e, diff));
}
 
int query(int node, int l, int r, int s, int e) {
    lazyUpdate(node, l, r);
    if (l > e || s > r) return 0;
    if (s <= l && r <= e) return tree[node];
 
    int m = (l + r) >> 1;
    return min(query(node << 1, l, m, s, e), query((node << 1+ 1, m + 1, r, s, e));
}
 
void init() {
    for (int i = 1; i <= N; i++) {
        update(11, N, i, N, 1);
    }
 
    for (int i = 0; i < N; i++) {
        update(11, N, list[i], N, -1);
    }
}
 
void print() {
    if (query(11, N, 1, N) >= 0cout << "TAK\n";
    else cout << "NIE\n";
}
 
void func() {
    init();
    print();
 
    int idx, x;
    while (M--) {
        cin >> idx >> x;
        update(11, N, list[idx - 1], N, 1);
        update(11, N, x, N, -1);
        list[idx - 1= x;
        print();
    }
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
    cin >> M;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 4442 빌보의 생일  (0) 2024.08.01
boj 1517 버블 소트  (0) 2024.06.16
boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 12895 화려한 마을  (0) 2022.09.07
boj 10167 금광  (0) 2022.09.02

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

 

12895번: 화려한 마을

첫 번째 줄에 N, T, Q (1 ≤ N ≤ 100,000, 1 ≤ T ≤ 30, 1 ≤ Q ≤ 100,000)이 공백을 구분으로 주어진다. 각각 천나라에 존재하는 집의 개수, 사용할 색의 개수, 작업의 개수를 의미한다. 두 번째 줄부터 작

www.acmicpc.net

출력은 l ~ r 구간에 칠해져 있는 색의 가짓수, 집에 칠할 수 있는 색은 최대 30가지입니다.

업데이트와 쿼리가 섞여 있으므로 오프라인 쿼리 및 mo's 알고리즘은 사용하지 못하고, 세그먼트 트리 lazy를 사용합니다.

트리에는 해당 구간에 칠해져 있는 색의 가짓수를 비트 필드로 저장합니다.

 

우선 모든 집에 1이 칠해져 있는 상태로 시작합니다. init 함수에서 초기화를 진행합니다.

type == C면 l ~ r 구간에 업데이트를 진행합니다.

원래 칠해져 있던 색을 지우고, 새로운 색을 채워야 하므로 tree[node]에 해당 색으로 변경해줍니다.

type == Q면 l ~ r 구간에 칠해져 있는 색의 가짓수를 출력합니다.

트리에는 비트 필드를 저장했으니 시프트연산을 하면서 1의 갯수를 출력합니다.

lazyUpdate는 모든 update, query 시 먼저 진행합니다.

 

이 문제는 l > r인 입력도 주어지니 l < r이 되도록 swap을 해야합니다.

 

 

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#define MAX 100001
using namespace std;
 
int tree[MAX * 4], lazy[MAX * 4];
int N, M, K;
 
void init(int node, int s, int e) {
    if (s == e) {
        tree[node] = 1;
        return;
    }
 
    int m = (s + e) / 2;
    init(node * 2, s, m);
    init(node * 2 + 1, m + 1, e);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
void lazyUpdate(int node, int s, int e) {
    if (!lazy[node]) return;
 
    tree[node] = lazy[node];
    if (s != e) {
        lazy[node * 2= lazy[node];
        lazy[node * 2 + 1= lazy[node];
    }
    lazy[node] = 0;
}
 
void update(int node, int s, int e, int l, int r, int k) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return;
    if (l <= s && e <= r) {
        lazy[node] = (1 << k);
        lazyUpdate(node, s, e);
        return;
    }
 
    int m = (s + e) / 2;
    update(node * 2, s, m, l, r, k);
    update(node * 2 + 1, m + 1, e, l, r, k);
    tree[node] = tree[node * 2| tree[node * 2 + 1];
}
 
int query(int node, int s, int e, int l, int r) {
    lazyUpdate(node, s, e);
    if (s > r || l > e) return 0;
    if (l <= s && e <= r) return tree[node];
 
    int m = (s + e) / 2;
    return query(node * 2, s, m, l, r) | query(node * 2 + 1, m + 1, e, l, r);
}
 
void func() {
    char type;
    int l, r, k;
    while (K--) {
        cin >> type;
        if (type == 'C') {
            cin >> l >> r >> k;
            if (l > r) swap(l, r);
            update(11, N, l, r, k - 1);
        }
        else {
            cin >> l >> r;
            if (l > r) swap(l, r);
            int ret = query(11, N, l, r);
            int ans = 0;
            while (ret) {
                ans += (ret & 1);
                ret >>= 1;
            }
            cout << ans << '\n';
        }
    }
}
 
void input() {
    cin >> N >> M >> K;
    init(11, N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > SegmentTree' 카테고리의 다른 글

boj 1517 버블 소트  (0) 2024.06.16
boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 10167 금광  (0) 2022.09.02
boj 16993 연속합과 쿼리  (0) 2022.08.29
boj 12846 무서운 아르바이트  (0) 2022.08.19

+ Recent posts