BOJ 27232 - 청소

image.png

set 을 잘 활용하면 풀리는 문제

어떠한 $K$ 길이의 구간은 경로의 사슬을 이루는데, 슬라이딩 윈도우를 하며 제일 왼쪽 것을 경로에서 제거하고 그 옆에 있던 것 두 개를 이어주고(시작이나 끝일 경우 끊어주기만), 추가해주는 것도 유사하게 처리한다.

$O(n \log n)$에 풀린다.

void solve() {
   int n, k;
   cin >> n >> k;
   if (k == 1) {
      cout << 0;
      return;
   }
   vi a(n);
   fv(a);
   int ans = 2e15;
   vector<pi> idx;
   set < pi > s;
   for (int i = 0; i < k; i++) {
      idx.pb({a[i], i});
      s.insert({a[i], i});
   }
   sort(all(idx), greater<>());
   int tmp = 0;
   for (int i = 1; i < k; i++) {
      tmp += abs(idx[i].se - idx[i - 1].se);
   }
   ans = tmp;
   for (int i = k; i < n; i++) {
      // i - k 번째의 인접한 두 개의 거리만큼 정답에서 빼고
      // 그 두개의 거리만큼 정답에 더한다.
      //

      pi removed = {a[i - k], i - k};
      auto it = s.find(removed);
      auto nxt = it;
      auto prev = it;
      nxt++;
      if (it == s.begin()) {
         tmp -= abs(nxt->se - removed.se);
      } else if (nxt == s.end()) {
         prev--;
         tmp -= abs(prev->se - removed.se);
      } else {
         prev--;
         tmp -= abs(prev->se - removed.se);
         tmp -= abs(nxt->se - removed.se);
         tmp += abs(prev->se - nxt->se);
      }
      s.erase(it);

      pi added = {a[i], i};
      it = s.insert(added).fi;
      nxt = it, prev = it;
      nxt++;
      if (it == s.begin()) {
         tmp += abs(nxt->se - added.se);
      } else if (nxt == s.end()) {
         prev--;
         tmp += abs(prev->se - added.se);
      } else {
         prev--;
         tmp -= abs(prev->se - nxt->se);
         tmp += abs(added.se - nxt->se);
         tmp += abs(added.se - prev->se);
      }
      mina(ans, tmp);
      debug(s);
   }
   cout << ans;
}

Tags:

Categories:

Updated:

Comments