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.
- (More overhead) We can discretize the endpoints and use a difference array to find the cost of each segment.
- (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: