BOJ 26393 - Čokolade

image.png

$I-F$ 가 최소가 되어야 한다.

이득을 $F-I$ 라고 정의해보자.

$c_i \le k_j$ 인 걸 살 때 얻는 이득은 $-c_i$ 이다.

$c_i > k_j$ 인 걸 살 때 얻는 이득은 내가 $k_j$ 를 지불하고 $F$가 $c_i-k_j$ 를 지불하므로 $c_i-2k_j$ 이다.

정렬된 $c$ 를 $k_j$ 를 pivot으로 두고 두 구간으로 나눴을 때 왼쪽과 오른쪽의 끝부터 택하는게 항상 더 이득이다.

$q$가 작다면 이걸 투포인터로 찾을 수 있겠지만 이 문제에선 성질을 관찰해서 삼분탐색을 이용할 수 있다.

$f(r)$ 을 오른쪽 끝에서부터 $r$ 개를 택할 때, 그럼 왼쪽에선 $m_j-r$ 개만큼 택하고 이득값이라고 하자.

$f(r)$이 uni-modal함을 보일수있을까?

나는 일단 못보이겠다. 찍어서 맞췄다 ㅈㅅ. 하지만 밑에 해설을 보면 왜 uni-modal한지 알 수 있다.

에디토리얼 해설을 보면 삼분탐색 말고 이분탐색으로만 풀 수 있다.

일단 왼쪽 초콜릿을 $m$개 고르고 오른쪽 초콜릿을 몇개를 취할지를 이분탐색으로 구하면 된다.

이분탐색이 끝나는 지점은 오른쪽 초콜릿에서 $x$개를 취했을 때 그걸 취했을 때 더 이득이 되지 않는 시점이다.

그 이전 초콜릿까지만 오른쪽에서 취해주면 된다.

아래는 삼분탐색 코드다.

void solve() {  
   int n, q;  
   cin >> n >> q;  
   vi c(n), p(n + 1);  
   fv(c);  
   sort(all(c));  
   for (int i = 0; i < n; i++) p[i + 1] = p[i] + c[i];  
   auto sum = [&](int l, int r) { return p[r + 1] - p[l]; };  
  
   while (q--) {  
      int k, m;  
      cin >> k >> m;  
      int pivot = ubi(c, k);  
      int L = pivot, R = n - pivot;  
      int l = max<int>(0, m - L), r = min(m, R);  
      auto val = [&](int x) -> int {  
         int left = m - x;  
         int right = x;  
         int ret = -sum(0, left - 1);  
         ret += sum(n - right, n - 1) - 2 * k * right;  
         return ret;  
      };  
      int ans = -2e18;  
      while (l < r - 3) {  
         int p = (l * 2 + r) / 3, q = (l + 2 * r) / 3;  
         int pv = val(p), qv = val(q);  
         if (pv > qv) r = q;  
         else l = p;  
      }  
      for (int i = l; i <= r; i++)maxa(ans, val(i));  
      cout << -ans << endl;  
   }  
}

Tags:

Categories:

Updated:

Comments