Bessie is going on a hiking excursion! The trail that she is currently traversing consists of NN checkpoints labeled 1…N1…N (1≤N≤1051≤N≤105).
There are KK (1≤K≤1051≤K≤105) tickets available for purchase. The ii-th ticket can be purchased at checkpoint cici (1≤ci≤N1≤ci≤N) for price pipi (1≤pi≤1091≤pi≤109) and provides access to all of checkpoints [ai,bi][ai,bi] (1≤ai≤bi≤N1≤ai≤bi≤N). Before entering any checkpoint, Bessie must have purchased a ticket that allows access to that checkpoint. Once Bessie has access to a checkpoint, she may return to it at any point in the future. She may travel between two checkpoints to which she has access, regardless of whether their labels differ by 1 or not.
For each of i∈[1,N]i∈[1,N], output the minimum total price required to purchase access to both checkpoints 11 and NN if Bessie initially has access to only checkpoint ii. If it is impossible to do so, print −1−1 instead.
The first line contains NN and KK.Each of the next KK lines contains four integers cici, pipi, aiai, and bibi for each 1≤i≤K1≤i≤K.
NN lines, one for each checkpoint.
7 6 4 1 2 3 4 10 5 6 2 100 7 7 6 1000 1 1 5 10000 1 4 6 100000 5 6
-1 -1 -1 1111 10100 110100 -1
If Bessie starts at checkpoint i=4i=4, then one way for Bessie to purchase access to checkpoints 11 and NN is as follows:
Problem credits: Benjamin Qi
[/hide]
(Analysis by Timothy Qian)
Consider each ticket and each checkpoint as a node in a graph. We draw the following edges: An edge from checkpoint ii of weight pjpj to ticket jj if i=cji=cj, an edge from ticket ii to checkpoint jj of weight 00 if ai≤j≤biai≤j≤bi. Each time we go to ticket jj, it can be seen as activating an edge to ticket jj for a price pjpj. Then the goal is for each ii to pay the minimum to activate some edges such that starting from checkpoint ii, we can visit checkpoint 11 and checkpoint NN.
Now consider the optimal set of edges that we pick. Say we start at checkpoint ss. The edges we picked must form a path from ss to 11 and ss to NN. Consider the nodes we visit in the path from checkpoint ss to checkpoint 11 in that order and call this list LL, and similarly for the nodes we visited on the path from ss to checkpoint NN and call this RR. Consider indices i,ji,j such that L[i]=R[j]L[i]=R[j], but for any x>i,y>jx>i,y>j, we have L[x]≠R[y]L[x]≠R[y]. This can be viewed as the "last common node" of L,RL,R. This exists as long as L,RL,R have at least one node in common, which they do, namely checkpoint ss. Note that i,ji,j need not be unique, but this does not matter. Let z=L[i]=R[j]z=L[i]=R[j]. Then the rest of list LL after index ii consists of a path from zz to 11. Similarly, the rest of list RR consists of a path from zz to NN. The first part of list LL consists of a path from ss to zz, and similarly for the first part of list RR. Note that the paths from zz to 11 and zz to NN do not contain any tickets in common. So for every optimal set of edges that we pick, we can decompose that edges into three parts: a path from our starting checkpoint to a node zz, and two disjoint paths from zz to checkpoint 11 and to checkpoint NN.
We first compute the shortest paths from any node to checkpoint 11. The same can be analogously done for computing the shortest paths from any node to checkpoint NN. We instead reverse the edges, and compute the minimum distance from checkpoint 11 to any other node. The main idea is we use a multi-source Dijkstra's algorithm on a Segment Tree. We first initialize checkpoint 11 at a distance of 00, and every other node at a distance of infinity. We put all checkpoints in a minimum priority queue based on their distance. After popping the top checkpoint, let it be ii, we must find all tickets that have not yet been visited yet that contain ii. To do this, we put the tickets in a Segment Tree based on their index after sorting the tickets by ajaj, their left interval value. Then in each node of the Segment Tree, we store the maximum bjbj, or the right interval value, of all tickets in the contained interval. Now we can descend down the Segment Tree to remove all the intervals that contain cici. When we remove an interval from the Segment Tree, we can set its right coordinate to −1−1. Note that we don't actually need to add this ticket to the priority queue. This is because this ticket's distance must be the same as the distance to the checkpoint that is currently being processed. Thus, we can just process these removed tickets immediately. Overall, these Segment Tree operations will take O(NlogN)O(NlogN) time, because each interval gets removed exactly once. Running this Dijkstra's algorithm thus takes O(NlogN)O(NlogN) time.
Finally, let DL[i],DR[i]DL[i],DR[i] be the distances for going to checkpoint 11 and a ticket with checkpoint NN starting from node ii as computed above. Then we initialize each node ii at a distance of DL[i]+DR[i]DL[i]+DR[i]. Then we once again run the same Dijkstra's algorithm as above to compute the distance to each node. The final distances to each checkpoint stores the desired answers.
Benjamin + Timothy Qian's Code:
#include <bits/stdc++.h> using namespace std; struct Ticket { int c, p, a, b; }; struct SegmentTree { int n, sz; vector<int> mx; vector<Ticket> tickets; SegmentTree(vector<Ticket> tickets) : tickets(tickets) { n = 1; sz = (int)tickets.size(); while (n < sz) { n <<= 1; } mx.assign(2 * n, 0); for (int i = 0; i < n; ++i) { if (i < sz) { mx[i + n] = tickets[i].b; } else { mx[i + n] = -1; } } for (int i = n - 1; i >= 1; --i) { pull(i); } } void pull(int i) { mx[i] = max(mx[2 * i], mx[2 * i + 1]); } void remove(vector<int>& v, int p, int i = 1, int l = 0, int r = -1) { if (r == -1) { r += n; } if (l >= sz || tickets[l].a > p || mx[i] < p) { return; } else if (l == r) { mx[i] = -1; v.push_back(l); } else { int m = (l + r) >> 1; remove(v, p, 2 * i, l, m); remove(v, p, 2 * i + 1, m + 1, r); pull(i); } } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); const long long INF = 1e18; int n, k; cin >> n >> k; vector<Ticket> tickets(k); for (auto& t : tickets) { cin >> t.c >> t.p >> t.a >> t.b; --t.c, --t.a, --t.b; } sort(tickets.begin(), tickets.end(), [](const auto& l, const auto& r) { return l.a < r.a; }); auto dijkstra = [&](vector<long long> dist) { priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq; for (int i = n; i < n + k; ++i) { dist[tickets[i - n].c] = min(dist[tickets[i - n].c], dist[i] + tickets[i - n].p); } for (int i = 0; i < n; ++i) { if (dist[i] < INF) { pq.push({dist[i], i}); } } SegmentTree seg(tickets); while (!pq.empty()) { auto x = pq.top(); pq.pop(); if (x.first > dist[x.second]) { continue; } vector<int> removed; seg.remove(removed, x.second); for (int r : removed) { if (dist[r + n] > x.first) { dist[r + n] = x.first; if (dist[tickets[r].c] > x.first + tickets[r].p) { dist[tickets[r].c] = x.first + tickets[r].p; pq.push({dist[tickets[r].c], tickets[r].c}); } } } } return dist; }; vector<long long> start_left(n + k, INF); start_left[0] = 0; vector<long long> dist_left = dijkstra(start_left); vector<long long> start_right(n + k, INF); start_right[n - 1] = 0; vector<long long> dist_right = dijkstra(start_right); vector<long long> dist(n + k); for (int i = 0; i < n + k; ++i) { dist[i] = dist_left[i] + dist_right[i]; } dist = dijkstra(dist); for (int i = 0; i < n; ++i) { if (dist[i] < INF) { cout << dist[i] << '\n'; } else { cout << -1 << '\n'; } } }
Author's note: This was created at the same time as (and is vaguely related to) EGOI 2021 Lanterns.
[/hide]
© 2024. All Rights Reserved. 沪ICP备2023009024号-1