This submission is migrated from previous version of oj.uz, which used different machine for grading. This submission may have different result if resubmitted.
#include "walk.h"
#include <bits/stdc++.h>
using namespace std;
struct point {
int x, y, idx;
point() { }
point(int x, int y, int idx) { this->x = x, this->y = y, this->idx = idx; }
bool operator < (const point &r) const {
return x < r.x || (x == r.x && y < r.y);
}
bool operator == (const point &r) const {
return x == r.x && y == r.y;
}
};
long long dijkstra(int s, int g, vector<vector<pair<long long, int>>> G) {
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
vector<long long> dist(G.size(), -1);
pq.push({0, s}); dist[s] = 0;
while (!pq.empty()) {
auto top = pq.top();
pq.pop();
if (top.first != dist[top.second]) continue;
for (auto v : G[top.second]) {
if (dist[v.second] > top.first + v.first || dist[v.second] == -1) {
dist[v.second] = top.first + v.first;
pq.push({dist[v.second], v.second});
}
}
}
return dist[g];
}
void add_edge(point &x, point &y, vector<vector<pair<long long, int>>> &G) {
G[x.idx].emplace_back(abs(y.x - x.x) + abs(y.y - x.y), y.idx);
G[y.idx].emplace_back(abs(y.x - x.x) + abs(y.y - x.y), x.idx);
return;
}
void build_graph(vector<int> x, vector<int> h, vector<int> l, vector<int> r, vector<int> y, int &s, int &g, vector<vector<pair<long long, int>>> &G) {
int n = x.size(), m = y.size();
vector<vector<int>> building(n);
vector<vector<pair<int, int>>> event(n);
building[s].emplace_back(0);
building[g].emplace_back(0);
for (int i = 0; i < m; i++) {
building[l[i]].emplace_back(y[i]);
building[r[i]].emplace_back(y[i]);
event[l[i]].emplace_back(y[i], +1);
if (r[i] + 1 < n) event[r[i] + 1].emplace_back(y[i], -1);
}
multiset<int> alive; //find intersection directly above or below or on start points and endpoints of skywalks
for (int i = 0; i < n; i++) { //proof of correctness:
for (auto &e : event[i]) { //let s = 0 and g = n - 1
if (e.second == +1) alive.insert(e.first); //permuting all possible ways, we can always go right (shortest path)
else alive.erase(alive.find(e.first)); //if there is a backtrack (left) segment, continued by another segment
} //we can always directly go there without going left at all. QED.
vector<int> tmp = building[i]; //as we always go right, x-distance is always the same -> minimize y-distance
for (auto &b : building[i]) { //to minimize, we cannot use the same skywalk twice
if (b == 0) continue; //thus we should only switch only at start and endpoint,
auto it = alive.upper_bound(b); //and if there are start and end point of another segment directly above or below
if (it != alive.end()) tmp.emplace_back(*it); //if we switch to below, we can just switch at the end of the current skywalk
it = alive.lower_bound(b); //if above, switch at new segment start point
if (it != alive.begin()) tmp.emplace_back(*prev(it)); //applying all of this, we only need to use 6 intersections
} //on, directly above, and directly below the start and end points of a skywalk
sort(tmp.begin(), tmp.end());
tmp.resize(unique(tmp.begin(), tmp.end()) - tmp.begin());
while (!tmp.empty() && tmp.back() > h[i]) tmp.pop_back();
building[i] = tmp; //delete double counting
}
alive.clear(); //get intersection points closest to s and g for every skywalks
vector<pair<int, int>> height_building; //observation: there exists a pl (pivot left) and pr (pivot right) such that
vector<pair<int, pair<int, int>>> height_skywalk; //the shortest path = shortest path from s to pl + pl to pr + pr to g
for (int i = 0; i < n; i++) alive.insert(i); //we can model s to pl as only going left -> ignore eerything on the right,
for (int i = 0; i < n; i++) height_building.emplace_back(h[i], i); //and get all shortest path from s to 0
for (int i = 0; i < m; i++) height_skywalk.push_back({y[i], {l[i], r[i]}}); //then we can go from those shortest points from 0 to n
sort(height_building.begin(), height_building.end()); //and then from n to g (model the same, but from g to n-1)
sort(height_skywalk.begin(), height_skywalk.end());
for (int i = 0, loc = 0; i < m; i++) { //also model from s to n-1, and from g to 0 -> thus every possible left-right path is possible
while (loc < n && height_skywalk[i].first > height_building[loc].first) alive.erase(height_building[loc++].second); //(right-right-tight),
if (height_skywalk[i].second.first <= s && s <= height_skywalk[i].second.second) { //(right, right, left), (left, right, tight),
auto it = alive.lower_bound(s); //and (left, right, left). Midpath is always right -> get every point in 0...s and g...n-1 and get their shortest path
building[*it].emplace_back(height_skywalk[i].first); //from s, you can go left or right -> get nearest intersection point for every skywalk
if (it != alive.begin()) building[*prev(it)].emplace_back(height_skywalk[i].first); //like s = 0, g = n-1; you can ignore the opposite side
} //nearest intersection point of every skywalk becomes "startpoints" for those skywalks
if (height_skywalk[i].second.first <= g && g <= height_skywalk[i].second.second) { //ergo for s-right, and for g
auto it = alive.lower_bound(g);
building[*it].emplace_back(height_skywalk[i].first);
if (it != alive.begin()) building[*prev(it)].emplace_back(height_skywalk[i].first);
}
}
//build nodes and edges
vector<point> points; //thus for shortest path from s to g
for (int i = 0; i < n; i++) { //just need 18 intersection points for every skywalk
for (auto &j : building[i]) { //startpoint and endpoint (2), and nearest to s and to e from left and from right (4),
int num = points.size(); //also create intersections directly above or below those intersection points
points.emplace_back(x[i], j, num); //2 * 6 = 18 intersections for every skywalk
}
}
G.resize(points.size());
auto cmpx = [&](const point &x, const point &y) { return x.x < y.x || (x.x == y.x && x.y < y.y); };
auto cmpy = [&](const point &x, const point &y) { return x.y < y.y || (x.y == y.y && x.x < y.x); };
sort(points.begin(), points.end(), cmpx);
for (int i = 0; i < n; i++) {
int lo = lower_bound(points.begin(), points.end(), point(x[i], 0, -1), cmpx) - points.begin();
int hi = upper_bound(points.begin(), points.end(), point(x[i], h[i], -1), cmpx) - points.begin();
for (int j = lo + 1; j < hi; j++) add_edge(points[j - 1], points[j], G);
}
sort(points.begin(), points.end(), cmpy);
for (int i = 0; i < m; i++) {
int lo = lower_bound(points.begin(), points.end(), point(x[l[i]], y[i], -1), cmpy) - points.begin();
int hi = upper_bound(points.begin(), points.end(), point(x[r[i]], y[i], -1), cmpy) - points.begin();
for (int j = lo + 1; j < hi; j++) add_edge(points[j - 1], points[j], G);
}
s = points[lower_bound(points.begin(), points.end(), point(x[s], 0, -1), cmpy) - points.begin()].idx;
g = points[lower_bound(points.begin(), points.end(), point(x[g], 0, -1), cmpy) - points.begin()].idx;
return;
}
long long min_distance(vector<int> x, vector<int> h, vector<int> l, vector<int> r, vector<int> y, int s, int g) {
vector<vector<pair<long long, int>>> G;
build_graph(x, h, l, r, y, s, g, G);
return dijkstra(s, g, G);
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |