Submission #1161184

#TimeUsernameProblemLanguageResultExecution timeMemory
1161184wanglingyun9Walk (CEOI06_walk)C++17
40 / 100
124 ms38248 KiB
#include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <cassert> using namespace std; // ----- Structures for Buildings, Candidates, and Events ----- struct Building { int r; // right coordinate = max(x1,x2) int lower; // min(y1,y2) int upper; // max(y1,y2) int id; // building id (0-indexed) }; // Candidate point: either the upper-right (TU) or lower-right (TD) corner of a building, // or the dummy candidate for the destination. struct Candidate { int x, y; // grid coordinates int type; // 0: TU, 1: TD, 2: dummy (destination) int bID; // building id (for TU and TD); -1 for dummy int nxt; // the "next" building id (computed by sweep–line); -1 means ε. // DP fields: long long dp; // best (minimum) Manhattan distance from S=(0,0) to this candidate along a special path. int parent; // parent candidate index (for path reconstruction), or -1 if coming directly from S. }; // An event for the sweep–line (to compute "next" for candidates). // There are four kinds: // type 0: Activation of building at y = building.lower // type 1: Deactivation of building at y = building.upper // type 2: Query for TD candidate (at y = candidate.y, which equals building.lower - 1) // type 3: Query for TU candidate (at y = candidate.y, which equals building.upper + 1) // (For the dummy candidate we treat it as a TU candidate.) struct Event { int y; int type; // 0: activate, 1: deactivate, 2: queryTD, 3: queryTU int x; // the x coordinate at which we update/query (this is the building's right side) int bID; // building id (for activate/deactivate events) int candIdx; // candidate index (for query events) }; // ----- Segment Tree (Tournament Tree) for range maximum query ----- // We store a pair: (val, bID) where val is the x coordinate (i.e. building right coordinate) // and bID is the building id. The merge returns the pair with larger 'val' (i.e. the right–most active building). struct SegNode { int val; int bID; }; struct SegmentTree { int size; vector<SegNode> tree; SegmentTree(int n) { size = 1; while(size < n) size *= 2; tree.assign(2 * size, {-1, -1}); } // Update position pos (0-indexed) to value (val, bID) void update(int pos, int val, int bID) { pos += size; tree[pos] = {val, bID}; for(pos /= 2; pos >= 1; pos /= 2) { if(tree[2*pos].val >= tree[2*pos+1].val) tree[pos] = tree[2*pos]; else tree[pos] = tree[2*pos+1]; } } // Query the maximum in interval [l, r] (inclusive) SegNode query(int l, int r) { SegNode res = {-1, -1}; l += size; r += size; while(l <= r) { if(l % 2 == 1) { if(tree[l].val > res.val) res = tree[l]; l++; } if(r % 2 == 0) { if(tree[r].val > res.val) res = tree[r]; r--; } l /= 2; r /= 2; } return res; } }; // ----- Helper: Manhattan distance between two points ----- inline long long manhattan(int x1, int y1, int x2, int y2) { return llabs(x1 - x2) + llabs(y1 - y2); } // ----- Main ----- int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int X, Y, N; cin >> X >> Y; cin >> N; vector<Building> buildings; buildings.reserve(N); for (int i = 0; i < N; i++){ int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; int r = max(x1, x2); int lower = min(y1, y2); int upper = max(y1, y2); buildings.push_back({r, lower, upper, i}); } // ----- Build candidate points ----- // For each building, we create two candidates: // TU: (r, upper + 1) // TD: (r, lower - 1) // And we also add one dummy candidate for the destination D = (X, Y). vector<Candidate> cand; cand.reserve(2 * N + 1); // We will also remember, for each building, the candidate indices for TU and TD. vector<int> candTU(N, -1), candTD(N, -1); for (int i = 0; i < N; i++){ // Upper–right candidate (TU) Candidate c; c.x = buildings[i].r; c.y = buildings[i].upper + 1; c.type = 0; // TU c.bID = buildings[i].id; c.nxt = -1; cand.push_back(c); candTU[buildings[i].id] = cand.size() - 1; // Lower–right candidate (TD) c.x = buildings[i].r; c.y = buildings[i].lower - 1; c.type = 1; // TD c.bID = buildings[i].id; c.nxt = -1; cand.push_back(c); candTD[buildings[i].id] = cand.size() - 1; } // Add dummy candidate for destination D; we treat it as a TU candidate. Candidate dummy; dummy.x = X; dummy.y = Y; dummy.type = 2; // dummy dummy.bID = -1; dummy.nxt = -1; cand.push_back(dummy); int dummyIdx = cand.size() - 1; // ----- Build events for the sweep–line (to compute candidate.nxt) ----- // For each building, add: // Activation event at y = lower, type 0. // Deactivation event at y = upper, type 1. // For each candidate from a building: // For TD (type 1): add query event at y = (lower - 1) [i.e. candidate.y], type 2. // For TU (type 0): add query event at y = (upper + 1) [i.e. candidate.y], type 3. // For the dummy candidate (type 2), add query event at y = Y + 1, type 3. vector<Event> events; events.reserve(2 * N + cand.size()); for (int i = 0; i < N; i++){ // Activation event for building i at y = lower Event ev; ev.y = buildings[i].lower; ev.type = 0; // activate ev.x = buildings[i].r; ev.bID = buildings[i].id; ev.candIdx = -1; events.push_back(ev); // Deactivation event for building i at y = upper ev.y = buildings[i].upper; ev.type = 1; // deactivate ev.x = buildings[i].r; ev.bID = buildings[i].id; ev.candIdx = -1; events.push_back(ev); } // Candidate query events: for (int i = 0; i < (int)cand.size(); i++){ Event ev; // For candidates from buildings: if(cand[i].type == 0){ // TU candidate: query event type 3 at y = candidate.y (== upper+1) ev.y = cand[i].y; ev.type = 3; // query TU } else if(cand[i].type == 1){ // TD candidate: query event type 2 at y = candidate.y (== lower-1) ev.y = cand[i].y; ev.type = 2; // query TD } else if(cand[i].type == 2){ // For dummy candidate, treat as TU; add query event at y = Y+1. ev.y = Y + 1; ev.type = 3; // query TU } ev.x = cand[i].x; // candidate's x coordinate ev.candIdx = i; ev.bID = -1; events.push_back(ev); } // Sort events by y; if equal, by type (we assume lower type values come first) sort(events.begin(), events.end(), [](const Event &a, const Event &b) { if(a.y == b.y) return a.type < b.type; return a.y < b.y; }); // ----- Process events with a segment tree ----- // Domain for x: x can be from 0 up to max (we assume maxX = 1000001) const int MAXX = 1000002; SegmentTree seg(MAXX); // Initially, all positions are -1. for (int i = 0; i < (int)events.size(); i++){ if(events[i].type == 0) { // Activation: add building at position events[i].x seg.update(events[i].x, events[i].x, events[i].bID); } else if(events[i].type == 1) { // Deactivation: remove building at position events[i].x seg.update(events[i].x, -1, -1); } else if(events[i].type == 2 || events[i].type == 3) { // Query events for candidate. SegNode res = seg.query(0, events[i].x); int found = res.val; // if -1, then no building found. int foundBID = res.bID; cand[events[i].candIdx].nxt = foundBID; // will be -1 if not found. } } // ----- DP: compute shortest distance (and parent pointers) for each candidate. // Our recurrence is: // If cand.nxt == -1, then dp = cand.x + |cand.y| (distance from S = (0,0)). // Otherwise, let r = cand.nxt (a building id) and let P1 = candidate TU for building r, // P2 = candidate TD for building r. // Then: dp = min( dp[P1] + d(P1, cand), dp[P2] + d(P2, cand) ). // Since r < cand.x, these candidates come earlier when we sort by x. // Sort candidates by (x, then y) vector<int> order(cand.size()); for (int i = 0; i < (int)cand.size(); i++) order[i] = i; sort(order.begin(), order.end(), [&](int a, int b) { if(cand[a].x == cand[b].x) return cand[a].y < cand[b].y; return cand[a].x < cand[b].x; }); const long long INF = 1LL << 60; vector<long long> dp(cand.size(), INF); vector<int> par(cand.size(), -1); // For quick lookup: for each building id, record the candidate indices for TU and TD. // (They were computed earlier in candTU and candTD.) for (int idx : order) { // Base case: if no building is encountered when going left from this candidate. if(cand[idx].nxt == -1) { dp[idx] = (long long)cand[idx].x + llabs(cand[idx].y); par[idx] = -1; } else { int r = cand[idx].nxt; // building id from "next" // Use the two candidate points for building r. int idxTU = candTU[r]; int idxTD = candTD[r]; // They must have been processed already (since their x < cand[idx].x) long long opt1 = dp[idxTU] + manhattan(cand[idx].x, cand[idx].y, cand[idxTU].x, cand[idxTU].y); long long opt2 = dp[idxTD] + manhattan(cand[idx].x, cand[idx].y, cand[idxTD].x, cand[idxTD].y); if(opt1 <= opt2) { dp[idx] = opt1; par[idx] = idxTU; } else { dp[idx] = opt2; par[idx] = idxTD; } } } // Find the dummy candidate (destination) int destIdx = -1; for (int i = 0; i < (int)cand.size(); i++){ if(cand[i].type == 2) { destIdx = i; break; } } assert(destIdx != -1); // The answer (minimum path length) is dp[destIdx] long long ans = dp[destIdx]; // ----- Reconstruct the candidate chain (turning–points) ----- vector<int> chain; int cur = destIdx; while(cur != -1) { chain.push_back(cur); cur = par[cur]; } // Reverse the chain so that it goes from the candidate reached directly from S up to destination. reverse(chain.begin(), chain.end()); // Build the polyline: start with S = (0,0) then the candidate points in order. vector<pair<int,int>> polyline; polyline.push_back({0, 0}); for (int idx : chain) polyline.push_back({cand[idx].x, cand[idx].y}); // ----- Compress the polyline into alternating horizontal and vertical segments ----- // (Merge consecutive segments that are collinear.) vector<pair<int,int>> points; points.push_back(polyline[0]); for (int i = 1; i < (int)polyline.size(); i++){ // If the last two points and the new point are collinear (in a horizontal or vertical direction), merge. if(points.size() >= 2) { auto &p1 = points[points.size()-2]; auto &p2 = points[points.size()-1]; auto &p3 = polyline[i]; // Check if p1->p2 and p2->p3 are parallel if((p2.first - p1.first == 0 && p3.first - p2.first == 0) || (p2.second - p1.second == 0 && p3.second - p2.second == 0)) { points.back() = p3; continue; } } points.push_back(polyline[i]); } // Now, segments are differences between consecutive points in 'points' vector<pair<int,int>> segments; for (int i = 0; i < (int)points.size()-1; i++){ int dx = points[i+1].first - points[i].first; int dy = points[i+1].second - points[i].second; segments.push_back({dx, dy}); } // ----- Output the solution ----- cout << ans << "\n"; cout << segments.size() << "\n"; for (auto &seg : segments) cout << seg.first << " " << seg.second << "\n"; return 0; }
#Verdict Execution timeMemoryGrader output
Fetching results...