BOJ 15968 - 물병 잡기

image.png

항상 모든 물병은 순서대로 세워지는 점을 이용한다.

따라서 시간 $t$에 어떤 물병이 어느 위치에 있는지 구할 수 있다면 이분탐색으로 어떤 물병부터 어떤 물병까지 그 범위안에 있는지를 알 수 있다.

일단 $0$번째 물병은 $d_0$ 주기로 재혁이의 바로 뒤로 이동한다.

그 시간을 $t_i$ 라고 하자. 그럼 그 다음부터는

$$ t_i=\left\lceil \dfrac{d_i}{t_{i-1}} \right\rceil \cdot t_{i-1} $$

이다.

바로 직전 물병이 움직이는 주기 $t_{i-1}$ 는 곧 그 직전 물병이 움직인 위치도 의미하기 때문에 $d_i$ 만큼 움직이기 위해 필요한 그 주기의 개수에 그 주기마다 직전 물병이 움직인 거리를 곱하면 현재 물병의 주기가 나온다.

그럼 $i$ 번째 물병이 시간 $T$에 있는 위치는 $-i+\left\lfloor \dfrac{T}{t_i} \right\rfloor \cdot t_i$ 이다.

void solve() {  
   int n, q;  
   cin >> n >> q;  
   vi d(n);  
   fv(d);  
   vi t(n);  
   t[0] = d[0];  
   for (int i = 1; i < n; i++) {  
      t[i] = (d[i] + t[i - 1] - 1) / t[i - 1] * t[i - 1];  
   }  
   auto pos_in_t = [&](int i, int time) {  
      return -(i + 1) + time / t[i] * t[i];  
   };  
  
   for (int i = 0; i < q; i++) {  
      int time, L, R;  
      cin >> time >> L >> R;  
  
      int tmp = 0;  
      if (L <= time && time <= R) tmp = 1;  
  
      int l = 0, r = n - 1, sp = -1;  
      while (l <= r) {  
         int mid = l + r >> 1;  
         if (pos_in_t(mid, time) <= R) sp = mid, r = mid - 1;  
         else l = mid + 1;  
      }  
      if (sp != -1 && pos_in_t(sp, time) >= L) {  
         l = sp, r = n - 1;  
         int ep = -1;  
         while (l <= r) {  
            int mid = l + r >> 1;  
            if (pos_in_t(mid, time) >= L) ep = mid, l = mid + 1;  
            else r = mid - 1;  
         }  
         assert(~ep);  
         tmp += ep - sp + 1;  
      }  
  
      cout << tmp << endl;  
   }  
}

Tags:

Categories:

Updated:

Comments