문제
상근이는 유전자 조작을 통해 줄기 두 개로 이루어진 식물을 만들었다. 이 식물은 줄기의 x좌표 L, R과 높이 H로 나타낼 수 있다. 아래 그림은 L=2, R=5, H=4인 식물이다.
상근이는 매일 매일 화단에 식물을 하나씩 심는다. 첫 번째 날에 심은 식물의 높이는 1이고, 그 다음날에 심은 식물은 전날에 심은 식물의 높이보다 1 크다.
새 식물의 줄기가 다른 식물의 수평 선분과 교차하는 경우가 있다. 이러한 경우에 그 위치에는 꽃이 하나 피게 된다. (이미 꽃이 있는 경우에는 꽃이 더 피지 않는다) 점에서 접하는 경우에는 꽃이 피지 않는다.
아래 그림은 예제를 나타낸 것이다.
모든 식물의 좌표가 주어졌을 때, 매일 매일 피는 꽃의 개수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 식물을 심은 날의 수 N (1 ≤ N ≤ 100,000)이 주어진다.
다음 N개 줄에는 매일 매일 심은 식물의 두 줄기 좌표 L과 R이 주어진다. (1 ≤ L < R ≤ 100,000)
출력
총 N개의 줄에 매일 매일 핀 꽃의 수를 출력한다.
풀이 과정
느리게 갱신되는 세그먼트 트리 알고리즘을 이용하면 쉽게 풀 수 있다.
식물이 자라는 L, R 사이에서, 꽃이 필 수 있는 곳인 L+1 ~ R-1 에 1을 증가시키고, L과 R 지점에서 이 위치의 식물 갯수를 구하고 0으로 갱신한다.
즉, tree에는 기존에 얼마나 식물이 자랐는지 ( 이곳에 얼마나 꽃을 피울 수 있는지 ) 를 저장해서, L, R 지점의 노드만 가져와 꽃을 피운 후 그 지점을 0으로 갱신해준다. 한 번 식물이 자랄 때 연속된 구간에 자라기 때문에 구간을 한번에 업데이트하는 lazy가 필요하다.
#include <stdio.h>
#define SIZE 100001
typedef long long LL;
LL arr[SIZE] = {0,};
LL tree[SIZE * 4] = {0,};
LL lazy[SIZE * 4] = {0,};
void update_lazy(int start, int end, int idx) {
if (lazy[idx] != 0) {
tree[idx] += (end - start + 1) * lazy[idx];
if (start != end) {
lazy[idx * 2] += lazy[idx];
lazy[idx * 2 + 1] += lazy[idx];
}
lazy[idx] = 0;
}
}
void update_range(int start, int end, int idx, int left, int right) {
update_lazy(start, end, idx);
if (end < left || right < start) return;
if (left <= start && end <= right) {
tree[idx] += (end - start + 1);
if (start != end) {
lazy[idx * 2] += 1;
lazy[idx * 2 + 1] += 1;
}
return;
}
int mid = (start + end) / 2;
update_range(start, mid, idx * 2, left, right);
update_range(mid + 1, end, idx * 2 + 1, left, right);
tree[idx] = tree[idx * 2] + tree[idx * 2 + 1];
}
void update_point(int start, int end, int idx, int what) {
update_lazy(start, end, idx);
if (what < start || end < what) return;
if (start == end) {
tree[idx] = 0;
return;
}
int mid = (start + end) / 2;
update_point(start, mid, idx * 2, what);
update_point(mid + 1, end, idx * 2 + 1, what);
tree[idx] = tree[idx * 2] + tree[idx * 2 + 1];
}
LL getVal(int start, int end, int idx, int what) {
update_lazy(start, end, idx);
if (what < start || end < what) return 0;
if (start == end) return tree[idx];
int mid = (start + end) / 2;
return getVal(start, mid, idx * 2, what) + getVal(mid + 1, end, idx * 2 + 1, what);
}
int main(void) {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int l, r;
scanf("%d %d", &l, &r);
if (l + 1 < r) update_range(0, SIZE - 1, 1, l, r - 2);
LL cnt = getVal(0, SIZE - 1, 1, l - 1) + getVal(0, SIZE - 1, 1, r - 1);
printf("%lld\n", cnt);
update_point(0, SIZE - 1, 1, l - 1);
update_point(0, SIZE - 1, 1, r - 1);
}
return 0;
}
'-- 예전 기록 > BOJ' 카테고리의 다른 글
[ BOJ ] 15961 : 회전 초밥 ( GOLD 4 ) / Python (0) | 2023.03.26 |
---|---|
[ BOJ ] 17353 : 하늘에서 떨어지는 1, 2, ..., R-L+1개의 별 ( PLATINUM 2 ) / C (0) | 2023.03.24 |
[ BOJ ] 15686 : 치킨 배달 ( GOLD 5 ) / Python (0) | 2023.03.24 |
[ BOJ ] 1275 : 커피숍2 ( GOLD 1 ) / C (0) | 2023.03.16 |
[ BOJ ] 2357 : 최솟값과 최댓값 ( GOLD 1 ) / C (0) | 2023.03.16 |