티스토리 뷰

Computer Science/BOJ

BOJ 32069. 가로등

yeongminb 2024. 7. 24. 11:03

문제 링크 

 

문제 풀이

  이 문제는 수직선 위에서 가로등의 위치들이 주어질 때 각 수직선 상의 위치마다 밝기를 작은 것부터 $K \leqslant 3\times 10^5$개 출력하는 문제이다. 

 

 일단 $ L \leqslant 10^{18} $ 이므로 배열에 각 위치들을 배열에 저장하거나, 완전탐색을 할 경우 MLETLE가 날 수밖에 없다. 그래서 필요한 위치들의 밝기만 저장하거나 다른 방법이 필요하다.

 

 가장 처음에 생각했던 것은 인접한 가로등 사이의 구간 길이를 저장하는 것이다. 구간의 길이의 개수는 항상 $N+1$개이고, $N \leqslant 3\times 10^{5} $ 이기 때문에 완전탐색을 해도 별 문제가 없고, 구간의 길이가 각 위치들의 밝기를 특정해주기 때문이다. 예를 들어서 구간의 길이가 5라고 하면 밝기는 아래와 같이 특정된다.

위치 x x+1 x+2 x+3 x+4 x+5 x+6
밝기 0 1 2 3 2 1 0
가로등이 있는가? O           O

 

 조금 더 구체적으로 써보면

let) 구간 길이 x
if  x % 2 == 0  $\Rightarrow$ 밝기 1 ~ $\frac{x}{2}$ 2개씩 존재
else if x % 2 == 1 $\Rightarrow$ 밝기 1 ~ $\frac{x-1}{2}$ 2개씩 존재 + 밝기 $\frac{x+1}{2}$ 1개 존재

 

추가적으로 위치 0부터 최초의 가로등까지의 구간이나 마지막 가로등부터 위치 $L$까지의 구간은 따로 고려해줘야하는데 이때는 구간의 길이가 k이면 그 구간에 밝기 1 ~ k까지 1개씩 존재한다.

 

 이제 이것을 이용해서 밝기 1인 칸의 개수, 밝기 2인 칸의 개수, ... 등을 알면 된다. 이를 수식으로 써보자.

 

let) $L_{k}$ = (밝기가 k인 칸의 개수)

let) $I_{x}$ = (구간의 길이가 x인 구간의 개수)

let) $X_{0} = A[0]$

let) $X_{1} = L - A[N-1]$

$$ L_{k} = I_{2k-1}\space + 2\sum_{i\geq2k}I_{i} + \begin{cases}2 & {X_{0},X_{1}\geq k}\\1 & {X_{i}\geq k\space and \space X_{1-i}<k\space(i\in{0,1})}\\0 & {X_{0},X_{1}< k}\end{cases} $$

 

 이제 밝기 k를 1부터 높여가며 차례대로 확인하고 지금까지 출력한 개수가 $K$개가 되면 종료해주면 된다. 시간복잡도는 밝기가 최대 K까지 있을 수 있기 때문에 $O(K)$만큼 반복문을 수행하고, 반복문 안에서 lowerbound를 찾는 과정이 $O(logN)$ 이므로 전체는 $O(KlogN)$ 이다.

 

P.S.) 문제 풀 땐 생각을 못했는데 가로등들을 기준으로 BFS를 수행해줘서 푸는 게 정풀이라고 한다,,

 

코드

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const ll LMAX = 1000000000000000000;
const int NMAX = 300000, KMAX = 500000;
ll L, A[NMAX + 10], inter[NMAX + 10];
int N, K;

void input() {
    cin >> L >> N >> K;
    for (int i = 0; i < N; i++) {
        cin >> A[i];
    }
}

void init() {
    inter[0] = A[0];          
    inter[1] = L - A[N - 1]; 
    for (int i = 2; i <= N; i++) {
        inter[i] = A[i - 1] - A[i - 2] - 1;  
    }
    sort(inter + 2, inter + N + 1); 
}

void solve() {
    if (N >= K) {
        for (int i = 0; i < K; i++) cout << 0 << "\n"; 
        return;
    } else {
        for (int i = 0; i < N; i++) cout<<0<<"\n";
        K -= N;
        int K_prime = K;
        for (ll i = 1; i <= 2*K; i+=2) {
            ll n = (i + 1) / 2;
            if (inter[0] >= n && i%2==1) {
                cout<<n<<"\n"; K_prime--;
                if (K_prime == 0) return;
            }
            if (inter[1] >= n && i%2==1) {
                cout << n << "\n"; K_prime--;
                if (K_prime == 0) return;
            }
            int lb = lower_bound(inter + 2, inter + N + 1, i) - inter;
            if(lb>=N+1) continue;
            if (inter[lb] == i) {
                int lbprime = lower_bound(inter + 2, inter + N + 1, i + 1) - inter;
                for (int j = lb; j < lbprime; j++) {
                    if (K_prime == 0) return;
                    cout << n << "\n"; K_prime--;
                }
                for (int j = lbprime; j <= N; j++) {
                    if (K_prime == 0) return;
                    cout << n << "\n"; K_prime--;
                    if (K_prime == 0) return;
                    cout << n << "\n"; K_prime--;
                }
            } else {
                for (int j = lb; j <= N; j++) {
                    if (K_prime == 0) return;
                    cout << n << "\n"; K_prime--;
                    if (K_prime == 0) return;
                    cout << n << "\n"; K_prime--;
                }
            }
        }
    }
}

int main() {
    cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);
    input();
    init();
    solve();
}

 

'Computer Science > BOJ' 카테고리의 다른 글

BOJ 2887. 행성터널  (0) 2024.08.17
BOJ 1202. LOPOV  (0) 2024.08.06
BOJ 19942. 다이어트  (0) 2024.07.13
BOJ 14500. 테트로미노  (1) 2024.06.08
BOJ 28325. 호숫가의 개미굴  (1) 2024.05.25
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/04   »
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
글 보관함