Submission #1161185

#TimeUsernameProblemLanguageResultExecution timeMemory
1161185wanglingyun9Walk (CEOI06_walk)C++17
40 / 100
118 ms39204 KiB
#include <bits/stdc++.h>
using namespace std;
 
// ---------------- Structures ------------------
 
struct Building {
    int r;      // right coordinate = max(x1,x2)
    int lower;  // min(y1,y2)
    int upper;  // max(y1,y2)
    int id;     // building id
};
 
// Candidate point (a “corner”)
struct Candidate {
    int x, y;    // coordinates
    int type;    // 0: TU (upper candidate), 1: TD (lower candidate)
    int bID;     // building id (for TU/TD); for dummy candidate (destination) we set bID = -1.
    int nxt;     // the building id (T) encountered when “looking left” from this candidate; -1 if none.
    long long dp;  // DP value: minimum distance from S=(0,0) to this candidate along a special path.
    int parent;    // predecessor candidate index in the DP chain.
};
 
// Sweep–line event
// For buildings: type 0 = activation (at y = lower), type 1 = deactivation (at y = upper).
// For candidate queries: type 2 = query for TD candidate (at candidate.y), type 3 = query for TU candidate (at candidate.y).
struct Event {
    int y;
    int type;    // 0: activate, 1: deactivate, 2: query TD, 3: query TU
    int x;       // x coordinate of the event (for building events: building r)
    int bID;     // building id for activate/deactivate; -1 otherwise.
    int candIdx; // candidate index (for query events)
};
 
// ---------------- Segment Tree ------------------
 
struct SegNode {
    int val; // store the x coordinate (r value) of the building currently active at that index.
    int bID; // building id.
};
 
struct SegmentTree {
    int size;
    vector<SegNode> tree;
    SegmentTree(int n) {
        size = 1;
        while(size < n) size *= 2;
        tree.assign(2 * size, {-1, -1});
    }
    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];
        }
    }
    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 ------------------
 
inline long long manhattan(int x1, int y1, int x2, int y2) {
    return (long long) abs(x1 - x2) + abs(y1 - y2);
}
 
// ---------------- Main ------------------
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int X, Y, N;
    cin >> X >> Y >> 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 T, create:
    //   TU: (T.r, T.upper+1) and TD: (T.r, T.lower-1)
    // Also add dummy candidate for destination D = (X,Y).
    vector<Candidate> cand;
    cand.reserve(2 * N + 1);
    // To quickly find a building’s candidates:
    vector<int> candTU(N, -1), candTD(N, -1);
    for (int i = 0; i < N; i++){
        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;
        c.dp = 0;
        c.parent = -1;
        cand.push_back(c);
        candTU[buildings[i].id] = cand.size() - 1;
 
        c.x = buildings[i].r;
        c.y = buildings[i].lower - 1;
        c.type = 1; // TD
        c.bID = buildings[i].id;
        c.nxt = -1;
        c.dp = 0;
        c.parent = -1;
        cand.push_back(c);
        candTD[buildings[i].id] = cand.size() - 1;
    }
    // Dummy candidate for destination.
    Candidate dummy;
    dummy.x = X; dummy.y = Y;
    // Treat dummy as a TU candidate.
    dummy.type = 0;
    dummy.bID = -1;
    dummy.nxt = -1;
    dummy.dp = 0;
    dummy.parent = -1;
    cand.push_back(dummy);
    int dummyIdx = cand.size() - 1;
 
    // ---------------- Build Sweep–line Events ------------------
    vector<Event> events;
    events.reserve(2 * N + cand.size());
    for (int i = 0; i < N; i++){
        Event ev;
        ev.y = buildings[i].lower;
        ev.type = 0; // activation
        ev.x = buildings[i].r;
        ev.bID = buildings[i].id;
        ev.candIdx = -1;
        events.push_back(ev);
 
        ev.y = buildings[i].upper;
        ev.type = 1; // deactivation
        ev.x = buildings[i].r;
        ev.bID = buildings[i].id;
        ev.candIdx = -1;
        events.push_back(ev);
    }
    // For candidate queries:
    // For TU (type==0) candidates: query at y = candidate.y with event type 3.
    // For TD (type==1) candidates: query at y = candidate.y with event type 2.
    for (int i = 0; i < (int)cand.size(); i++){
        Event ev;
        ev.y = cand[i].y;
        if(cand[i].type == 0) { // TU (or dummy)
            ev.type = 3; // query TU
        } else { // TD
            ev.type = 2; // query TD
        }
        ev.x = cand[i].x;
        ev.candIdx = i;
        ev.bID = -1;
        events.push_back(ev);
    }
    // Sort events by y; if equal, by type (activation < query < deactivation)
    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 Segment Tree ------------------
    const int MAXX = 1000002;
    SegmentTree seg(MAXX);
    for(auto &ev : events){
        if(ev.type == 0) { // activation
            seg.update(ev.x, ev.x, ev.bID);
        } else if(ev.type == 1) { // deactivation
            seg.update(ev.x, -1, -1);
        } else if(ev.type == 2 || ev.type == 3) { // query events
            SegNode res = seg.query(0, ev.x);
            cand[ev.candIdx].nxt = res.bID; // -1 if none found
        }
    }
 
    // ---------------- DP: Process Candidates in increasing order of x (and type order)
    // Order: sort by (x ascending, then type where TU (0) comes before TD (1))
    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].type < cand[b].type;
        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);
 
    // Base: For any candidate with no blocking building (nxt == -1), dp = x + |y|
    for (int idx : order) {
        if(cand[idx].nxt == -1) {
            dp[idx] = cand[idx].x + (long long) abs(cand[idx].y);
            par[idx] = -1;
        } else {
            int r = cand[idx].nxt; // building id that blocks candidate idx
            int idxTU = candTU[r];
            int idxTD = candTD[r];
            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;
            }
        }
        // If two candidates come from the same building (i.e. same x and same bID, and bID != -1),
        // update both to the same (minimum) dp value.
        if(cand[idx].bID != -1) {
            // Look ahead in order for another candidate from the same building
            // (since buildings produce exactly two candidates).
            // (They have the same x; our sort order ensures TU comes before TD.)
            // We can update both.
            // Find next candidate with same x and same building id.
            int j = idx + 1;
            if(j < (int)order.size()){
                int idx2 = order[j];
                if(cand[idx2].bID == cand[idx].bID && cand[idx2].x == cand[idx].x) {
                    long long bestVal = min(dp[idx], dp[idx2]);
                    dp[idx] = dp[idx2] = bestVal;
                }
            }
        }
    }
    // Save DP and parent info back to candidates.
    for (int i = 0; i < (int)cand.size(); i++){
        cand[i].dp = dp[i];
        cand[i].parent = par[i];
    }
 
    // The answer is the dp for the dummy candidate.
    long long ans = cand[dummyIdx].dp;
 
    // ---------------- Reconstruct the Candidate Chain ------------------
    vector<int> chain;
    int cur = dummyIdx;
    while(cur != -1) {
        chain.push_back(cur);
        cur = cand[cur].parent;
    }
    reverse(chain.begin(), chain.end());
    // Now chain[0] will be the candidate reached directly from S.
    // (S itself is not among the candidates.)
 
    // ---------------- Reconstruct the Path Polyline ------------------
    // We now “simulate” the special path using a safe routing rule:
    // • From S = (0,0) to the first candidate P:
    //    – Since S is not attached to a building, we “exit” safely by moving vertically first,
    //      then horizontally.
    // • From a candidate (which is on a building’s boundary) to the next candidate,
    //    the only safe move is to continue horizontally (to stay on the building boundary)
    //    and then change vertically.
    vector<pair<int,int>> polyline;
    // Start at S = (0,0)
    polyline.push_back({0,0});
    // If there is at least one candidate in the chain, define P0 = chain[0]'s coordinates.
    // For leg 0 (from S to chain[0]): use vertical-then-horizontal.
    if(!chain.empty()){
        int idx = chain[0];
        int targetX = cand[idx].x;
        int targetY = cand[idx].y;
        // Vertical move from (0,0) to (0, targetY)
        polyline.push_back({0, targetY});
        // Horizontal move from (0, targetY) to (targetX, targetY)
        polyline.push_back({targetX, targetY});
    }
    // For subsequent legs (from candidate i to candidate i+1), use horizontal-first.
    for (size_t i = 0; i + 1 < chain.size(); i++){
        int currIdx = chain[i];
        int nextIdx = chain[i+1];
        int currX = cand[currIdx].x, currY = cand[currIdx].y;
        int nextX = cand[nextIdx].x, nextY = cand[nextIdx].y;
        // From current candidate, move horizontally to nextX while staying at currY.
        polyline.push_back({nextX, currY});
        // Then move vertically from (nextX, currY) to (nextX, nextY).
        polyline.push_back({nextX, nextY});
    }
 
    // ---------------- Compress Polyline Segments ------------------
    // Merge consecutive segments in the same direction.
    vector<pair<int,int>> segments;
    // First compute segments from polyline points.
    vector<pair<int,int>> pts = polyline;
    // Remove consecutive duplicate points.
    vector<pair<int,int>> clean;
    clean.push_back(pts[0]);
    for (size_t i = 1; i < pts.size(); i++){
        if(pts[i] != clean.back())
            clean.push_back(pts[i]);
    }
    for (size_t i = 0; i + 1 < clean.size(); i++){
        int dx = clean[i+1].first - clean[i].first;
        int dy = clean[i+1].second - clean[i].second;
        segments.push_back({dx, dy});
    }
    // Further compress if two consecutive segments are in the same direction.
    vector<pair<int,int>> comp;
    if(!segments.empty()){
        comp.push_back(segments[0]);
        for (size_t i = 1; i < segments.size(); i++){
            auto &last = comp.back();
            // Same direction if both dx are zero or both dy are zero.
            if((last.first == 0 && segments[i].first == 0) ||
               (last.second == 0 && segments[i].second == 0)){
                last.first += segments[i].first;
                last.second += segments[i].second;
            } else {
                comp.push_back(segments[i]);
            }
        }
    }
 
    // ---------------- Output ------------------
    cout << ans << "\n";
    cout << comp.size() << "\n";
    for (auto &seg : comp)
        cout << seg.first << " " << seg.second << "\n";
 
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...