Submission #604044

#TimeUsernameProblemLanguageResultExecution timeMemory
604044erekle모임들 (IOI18_meetings)C++17
60 / 100
5553 ms53368 KiB
#include "meetings.h"
#include <stack>
#include <algorithm>

using namespace std;

const long long INF = 1e18;
const int N = 1e5 + 1, LG = 17 + 1;
long long pre[N], suf[N];
long long mn[LG][N]; // sparse table of mins over pre[x] + suf[x] in range
long long queryMN(int l, int r) {
  int i = 0, p2 = 1;
  while (p2 <= r-l+1) ++i, p2 <<= 1;
  --i, p2 >>= 1;
  return min(mn[i][l], mn[i][r-p2+1]);
}

vector<long long> minimum_costs(vector<int> H, vector<int> L, vector<int> R) {
  int Q = L.size(), N = H.size();
  vector<long long> C(Q);

  if (*max_element(H.begin(), H.end()) <= 20) {
    // Convert from zero-based to one-based
    H.insert(H.begin(), 0);
    for (int &x : L) ++x;
    for (int &x : R) ++x;

    // Precalculate next and previous of each value from each position
    vector<int> nxt[21], prv[21];
    for (int i = 0; i <= 20; ++i) {
      prv[i] = vector<int>(1+N+1, 0);
      nxt[i] = vector<int>(1+N+1, N+1);
    }
    for (int i = 1; i <= N; ++i) {
      for (int j = 1; j <= 20; ++j) prv[j][i] = prv[j][i-1];
      prv[H[i]][i] = i;
    }
    for (int i = N; i >= 1; --i) {
      for (int j = 1; j <= 20; ++j) nxt[j][i] = nxt[j][i+1];
      nxt[H[i]][i] = i;
    }

    // Precalculate number with each maximum on each prefix and suffix
    vector<int> cntMaxPrefix[21]{}, cntMaxSuffix[21]{};
    for (int i = 1; i <= 20; ++i) {
      cntMaxPrefix[i].resize(1+N+1);
      cntMaxSuffix[i].resize(1+N+1);
    }
    for (int i = 1; i <= N; ++i) {
      cntMaxPrefix[H[i]][i] = 1;
      for (int j = 1; j <= H[i]; ++j) cntMaxPrefix[H[i]][i] += cntMaxPrefix[j][i-1];
      for (int j = H[i]+1; j <= 20; ++j) cntMaxPrefix[j][i] = cntMaxPrefix[j][i-1];
    }
    for (int i = N; i >= 1; --i) {
      cntMaxSuffix[H[i]][i] = 1;
      for (int j = 1; j <= H[i]; ++j) cntMaxSuffix[H[i]][i] += cntMaxSuffix[j][i+1];
      for (int j = H[i]+1; j <= 20; ++j) cntMaxSuffix[j][i] = cntMaxSuffix[j][i+1];
    }

    // Precalculate costs on prefix and suffix
    for (int i = 1; i <= N; ++i) {
      for (int j = 1; j <= 20; ++j) {
        pre[i] += cntMaxPrefix[j][i] * j;
        suf[i] += cntMaxSuffix[j][i] * j;
      }
      suf[i] -= H[i]; // prefix includes this value itself, suffix does not
      mn[0][i] = pre[i] + suf[i];
    }

    // Create sparse table for minimums over range
    for (int j = 1, p2 = 1; j < LG; ++j, p2 <<= 1) {
      for (int i = 1; i <= N; ++i) {
        if (i+p2 > N) mn[j][i] = mn[j-1][i];
        else mn[j][i] = min(mn[j-1][i], mn[j-1][i+p2]);
      }
    }

    // Answer each query
    for (int q = 0; q < Q; ++q) {
      int l = L[q], r = R[q];
      // Take next of each value from left and previous of each value from right
      int soFar = r+1;
      vector<pair<int, int>> starts;
      for (int j = 20; j >= 1; --j) {
        if (nxt[j][l] >= soFar) continue;
        soFar = nxt[j][l];
        starts.emplace_back(nxt[j][l], j);
      }
      reverse(starts.begin(), starts.end());
      
      soFar = l-1;
      vector<pair<int, int>> ends;
      for (int j = 20; j >= 1; --j) {
        if (prv[j][r] <= soFar) continue;
        soFar = prv[j][r];
        ends.emplace_back(prv[j][r], j);
      }

      vector<pair<int, int>> endsRemade;
      for (int start = l, i = 0; i < (int)ends.size(); ++i) {
        endsRemade.emplace_back(start, ends[i].second);
        start = ends[i].first+1;
      }
      ends = endsRemade;

      vector<pair<int, int>> merged;
      for (int i = 0; i < (int)starts.size(); ++i) merged.emplace_back(starts[i].first, starts[i].second);
      for (int i = 0; i < (int)ends.size(); ++i) merged.emplace_back(ends[i].first, -ends[i].second);
      sort(merged.begin(), merged.end());


      C[q] = INF;
      for (int i = 0, curL = 0, curR = 0; i < (int)merged.size(); ++i) {
        if (merged[i].second > 0) { // Start
          curL = merged[i].second;
        } else { // End
          curR = -merged[i].second;
        }
        if (i+1 < (int)merged.size() && merged[i+1].first == merged[i].first) continue;
        int rangeL = merged[i].first, rangeR = r;
        if (i+1 < (int)merged.size()) rangeR = merged[i+1].first-1;

        // now calculate best answer in this range
        long long best = queryMN(rangeL, rangeR); // min over pre + suf
        // subtract before l and after r
        for (int j = 1; j <= 20; ++j) {
          best -= cntMaxPrefix[j][l-1] * max(j, curL);
          best -= cntMaxSuffix[j][r+1] * max(j, curR);
        }
        C[q] = min(C[q], best);
      }
    }
  } else {
    for (int q = 0; q < Q; ++q) {
      int l = L[q], r = R[q];
      vector<long long> ans(r-l+1);
      stack<int> st;

      // Find answer contributions from left
      st.push(l-1);
      long long sum = 0;
      for (int i = l; i <= r; ++i) {
        while (st.top() >= l && H[st.top()] <= H[i]) {
          int x = st.top();
          st.pop();
          sum += (long long)(x-st.top())*(H[i]-H[x]);
        }
        st.push(i);
        sum += H[i];
        ans[i-l] += sum;
      }
      while (!st.empty()) st.pop();

      // Find answer contributions from right
      st.push(r+1);
      sum = 0;
      for (int i = r; i >= l; --i) {
        while (st.top() <= r && H[st.top()] <= H[i]) {
          int x = st.top();
          st.pop();
          sum += (long long)(st.top()-x)*(H[i]-H[x]);
        }
        st.push(i);
        sum += H[i];
        ans[i-l] += sum;
      }
      while (!st.empty()) st.pop();

      // Choose best answer
      long long minCost = INF;
      for (int i = l; i <= r; ++i) {
        ans[i-l] -= H[i]; // counted from both sides
        minCost = min(minCost, ans[i-l]);
      }
      C[q] = minCost;
    }
  }
  return C;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...