D - Snuke Prime Editorial by lucifer1004


Consider at first we have an infinite time span \([0,+\infty)\). The services will split the span into several non-overlapping segments. For example, if we have servcies \([1,4]\) and \([3,8]\), the final segments would be (note that we discard the leftmost segment, which should be \([0,0]\) in this case):

\[ [1,2],[3,4],[5,8],[9,+\infty) \]

Which can also be seen as:

\[ [1,3),[3,5),[5,9),[9,+\infty) \]

To represent these segments, we only need their left endpoints, that is, \(1,3,5,9\). And these left endpoints come from either \(a_i\) or \(b_i+1\). This is because only the start or the end of a service will make a difference. A service \([a_i,b_i]\) can also be seen as \([a_i,b_i+1)\), in which \(b_i+1\) is the first day that is not included, which means it is a start of a new segment. On the \(a_i\)-th day, the total cost will increase by \(c_i\), while on the \(b_i+1\)-th day, the total cost will decrease by \(c_i\).

After we get the segments, we need to calculate the cost for each segment, and compare it with \(C\), the price of the subscription. The time span of a segment can be easily determined from the start of the current segment and the start of the next segment.

To deal with the segments, we have two choices.

  1. (More overhead) We can discretize the endpoints and use a difference array to find the cost of each segment.
  2. (Clearer) We can use a map to store the change happening to the total cost on each critical day (\(a_i\) or \(b_i+1\), which is the start endpoint of a segment), then handle the segments one by one.

Both methods have a time complexity of \(\mathcal{O}(N\log N)\), since in both cases we need a sorted list of the timestamps.

Use discretization:

#include <iostream>
#include <map>
#include <set>
#include <vector>

using namespace std;
typedef long long ll;

int main() {
  int N;
  ll C;
  cin >> N >> C;
  vector<int> a(N), b(N), c(N);
  set<int> s;
  for (int i = 0; i < N; ++i) {
    cin >> a[i] >> b[i] >> c[i];

    // We only need a[i] and b[i]+1 to represent the final segments.
    // For example, [1, 4] and [3, 8] will make
    // [1, 2], [3, 4], [5, 8] and [9, +inf].
    // We need 1, 3, 5, and 9 to represent these segments.
    s.insert(a[i]), s.insert(b[i] + 1);
  }

  // Discretize the endpoints.
  int idx = 0;
  map<int, int> mp;
  for (int si : s)
    mp[si] = idx++;
  int M = s.size();
  vector<int> v(s.begin(), s.end());

  // Use a difference array to handle the services.
  vector<ll> diff(M);
  for (int i = 0; i < N; ++i)
    diff[mp[a[i]]] += c[i], diff[mp[b[i] + 1]] -= c[i];

  // Accumulate the difference array to get the value of each segment.
  // At the same time, add to the total cost.
  vector<ll> acc(M);
  acc[0] = diff[0];
  ll ans = 0;
  for (int i = 0; i < M - 1; ++i) {
    if (i >= 1)
      acc[i] = acc[i - 1] + diff[i];
    int span = v[i + 1] - v[i];
    ans += min(C, acc[i]) * span;
  }
  cout << ans;
}

Use map:

#include <iostream>
#include <map>
#include <set>
#include <vector>

using namespace std;
typedef long long ll;

int main() {
  int N;
  ll C;
  cin >> N >> C;
  vector<int> a(N), b(N), c(N);
  set<int> s;
  map<int, ll> changes;
  for (int i = 0; i < N; ++i) {
    cin >> a[i] >> b[i] >> c[i];

    // We only need a[i] and b[i]+1 to represent the final segments.
    // For example, [1, 4] and [3, 8] will make
    // [1, 2], [3, 4], [5, 8] and [9, +inf).
    // They can also be seen as [1, 3), [3, 5), [5, 9) and [9, +inf].
    // We need 1, 3, 5, and 9 to represent these segments.
    s.insert(a[i]), s.insert(b[i] + 1);

    // We use a map to store the change of cost on each critical day.
    changes[a[i]] += c[i];
    changes[b[i] + 1] -= c[i];
  }

  vector<int> v(s.begin(), s.end());
  int M = v.size();

  ll ans = 0, acc = 0;
  for (int i = 0; i < M - 1; ++i) {
    // Deal with the starting and ending of segments.
    acc += changes[v[i]];

    // Add to the total cost.
    ans += min(C, acc) * (v[i + 1] - v[i]);
  }
  cout << ans;
}

posted:
last update: