Submission #1245245

#TimeUsernameProblemLanguageResultExecution timeMemory
1245245countlessScissors and Tape (CEOI19_scissors)C++20
100 / 100
2 ms328 KiB
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef long double ld;

#define sp <<" "<<
#define endl "\n"

const ld EPS = 1e-3;

struct Point {
    ld x, y;
    
    Point(ld x = 0, ld y = 0) : x(x), y(y) {;;}

    bool operator==(Point p) const { return tie(x, y) == tie(p.x, p.y); }

    Point operator+(Point p) const { return Point(x+p.x, y+p.y); }
    Point operator-(Point p) const { return Point(x-p.x, y-p.y); }
    Point operator*(ld d) const { return Point(x*d, y*d); }
    Point operator/(ld d) const { return Point(x/d, y/d); }

    ld dist2() const { return x*x + y*y; }
    ld dist() const { return sqrtl(dist2()); }

    ld dot(Point p) const { return x*p.x + y*p.y; }
    ld angle() const { return atan2(y, x); }

    ld cross(Point p) const { return x*p.y - y*p.x; }
    ld cross(Point a, Point b) const { return (a-*this).cross(b-*this); }

    Point unit() const { return *this/dist(); }
    Point rotate(ld a) const { return Point(x*cos(a)-y*sin(a), x*sin(a)+y*cos(a)); }

    friend ostream& operator<<(ostream &os, Point p) {
        // return os << "(" << p.x << ", " << p.y << ")";
        return os << p.x << " " << p.y;
    }
};

struct Polygon {
    int id;
    vector<Point> poly;

    Polygon(int id = -1) : id(id) {;;}

    void input(ll sz) { 
        while (sz--) {
            ld x, y; cin >> x >> y;
            add(Point(x, y));
        }
    }

    void add(Point p) { poly.push_back(p); }
    void add(vector<Point> pp) { for (auto &p : pp) poly.push_back(p); }
    void set(vector<Point> pp) { poly = pp; }
    Point get(ll i) { return poly[i]; } // DANGER
    ll size() const { return poly.size(); }

    ld height() const { return (poly[0]-poly[1]).dist(); } // DANGER
    ld width() const { return (poly[1]-poly[2]).dist(); } // DANGER
    void shift(Point s) { for (auto &p : poly) p = p + s; }

    ld area() const {
        ld res = 0.0;
        int n = poly.size();

        for (int i = 0; i < n; i++) {
            int a = i, b = (i+1) % n;
            Point A = poly[a], B = poly[b];

            res += A.cross(B);
        }

        return abs(res / 2.0);
    }

    void clear() { id = -1, poly.clear(); }

    friend ostream& operator<<(ostream &os, Polygon poly) {
        int n = poly.size();
        os << n << " ";
        for (int i = 0; i < n; i++) {
            if (i) os << " ";
            os << poly.poly[i];
        }

        return os;
    }
};

int ID = 1;
vector<vector<Polygon>> construct, decomp;

void performCuts(const Polygon &poly, vector<Polygon> &pieces) {
    cout << "scissors" << endl;
    cout << poly.id << " " << pieces.size() << endl;
    // cerr << poly.id sp poly << endl;

    for (auto &piece : pieces) {
        piece.id = ID++;
        cout << piece << endl;
    }
}

void performPaste(Polygon &poly, vector<Polygon> &pieces) {
    cout << "tape" << endl;
    cout << pieces.size() << " ";

    for (auto &piece : pieces) {
        cout << piece.id << " ";
    }   cout << endl;

    for (auto &piece : pieces) {
        cout << piece << endl;
    }

    poly.id = ID++;
    cout << poly << endl;
}

// Check if P is in ABC
bool inTriangle(Point p, Point a, Point b, Point c) {
    ld area = abs(a.cross(b, c));
    ld a1 = abs(a.cross(b, p)), a2 = abs(b.cross(c, p)), a3 = abs(c.cross(a, p));
    return abs(area - (a1 + a2 + a3)) < EPS;
}

pair<Polygon, Polygon> decomposeTriangle(Polygon poly) {
    pair<Polygon, Polygon> res;

    int n = poly.size();
    for (int i = 0; i < n; i++) {
        int a = i, b = (i+1) % n, c = (i+2) % n;
        Point A = poly.get(a), B = poly.get(b), C = poly.get(c);

        if (A.cross(B, C) < 0) continue;

        bool convex = true;
        for (int j = 0; j < n; j++) {
            if (j == a or j == b or j == c) continue;
            if (inTriangle(poly.get(j), A, B, C)) {
                convex = false;
                break;
            }
        }

        if (!convex) continue;

        res.first.add({A, B, C});

        for (int j = 0; j < n; j++) {
            if (j == b) continue;
            res.second.add(poly.get(j));
        }

        return res;
    }

    assert(false);
    return res;
}

// Decompose a polygon into triangles
vector<Polygon> decomposeTriangles(Polygon poly, bool apply) {
    vector<Polygon> processed, unprocessed;

    unprocessed.push_back(poly);

    while (!unprocessed.empty()) {
        auto C = unprocessed.back(); unprocessed.pop_back();
        if (C.size() == 3) { processed.push_back(C); continue; }
        auto [A, B] = decomposeTriangle(C);

        A.size() > 3 ? unprocessed.push_back(A) : processed.push_back(A);
        B.size() > 3 ? unprocessed.push_back(B) : processed.push_back(B);
    }

    if (apply) performCuts(poly, processed);

    return processed;
}

// Compose rectangles from triangles (not optimal cut usage)
vector<Polygon> composeRectangles(vector<Polygon> &pieces, bool apply) {
    vector<Polygon> res;

    for (auto &piece : pieces) {
        Point A, B, C, D;

        int n = piece.size();
        assert(n == 3);

        // Find longest height to:
        // - guarantee altitude is inside the triangle
        // - splitting by the altitute results in two triangles
        for (int i = 0; i < n; i++) {
            int u = i, v = (i+1) % n, w = (i+2) % n;
            Point U = piece.get(u), V = piece.get(v), W = piece.get(w);
            if ((U-V).dist() > (A-B).dist()) { A = U, B = V, C = W; }
        }

        // Dear Prof. Schneider, I finally used something you taught me.
        // Project AC onto AB
        {
            Point u = B - A, v = C - A;
            D = A + (u * (v.dot(u) / u.dot(u)));
        }

        // 90-degree check
        assert((B-D).dot(C-D) < EPS);

        // Split into two right triangles
        vector<Polygon> right(2), cuts, pastes;
        Point E, F;

        right[0].add({A, D, C});
        right[1].add({C, D, B});

        if (apply) performCuts(piece, right);

        {
            // Split into a trapezoid and right triangle
            // Similar triangles
            Point M = (C+D) / 2;
            Point N = (A+C) / 2;

            vector<Polygon> split(2);

            split[0].add({A, D, M, N});
            split[1].add({N, M, C});

            if (apply) performCuts(right[0], split);
            else pastes.push_back(split[0]), pastes.push_back(split[1]);

            // Combine into a rectangle
            E = (A-D) + M;

            split[1].set({N, E, A});

            cuts.push_back(split[0]);
            cuts.push_back(split[1]);
        }

        {
            // Split into a trapezoid and right triangle
            // Similar triangles
            Point M = (C+D) / 2;
            Point N = (B+C) / 2;

            vector<Polygon> split(2);

            split[0].add({D, B, N, M});
            split[1].add({M, N, C});

            if (apply) performCuts(right[1], split);
            else pastes.push_back(split[0]), pastes.push_back(split[1]);

            // Combine into a rectangle
            F = (B-D) + M;

            split[1].set({F, N, B});

            cuts.push_back(split[0]);
            cuts.push_back(split[1]);
        }

        Polygon G;
        G.set({A, B, F, E});

        if (apply) performPaste(G, cuts);
        else construct.push_back(cuts), decomp.push_back(pastes);

        res.push_back(G);
    }

    return res;
}

vector<Polygon> normalizeRectangles(vector<Polygon> &pieces, vector<ld> &heights, bool apply) {
    vector<Polygon> res;

    assert(pieces.size() == heights.size());

    int n = pieces.size();
    for (int i = 0; i < n; i++) {
        Polygon piece = pieces[i];

        assert(piece.size() == 4);
        ld h = piece.height();

        // Two cases: smaller or larger

        if (h > heights[i]) {
            while (h > 2 * heights[i]) {
                Point A, B, C, D, E, F, G, H, S;
                A = piece.get(0);
                B = piece.get(1);
                C = piece.get(2);
                D = piece.get(3);
                S = D - A;
                E = (A + B) / 2;
                F = E + S;
                G = F + S;
                H = D + S;

                vector<Polygon> split(2);
                split[0].set({A, E, F, D});
                split[1].set({E, B, C, F});
                performCuts(piece, split);

                piece.set({A, E, G, H});
                split[1].set({D, F, G, H});
                performPaste(piece, split);

                h = piece.height();
            }
        }
        
        else {
            while (h < heights[i]) {
                Point A, B, C, D, E, F, G, H, S;
                A = piece.get(0);
                B = piece.get(1);
                C = piece.get(2);
                D = piece.get(3);
                S = B - A;
                E = (A + D) / 2;
                F = E + S;
                G = F + S;
                H = B + S;

                vector<Polygon> split(2);
                split[0].set({A, B, F, E});
                split[1].set({E, F, C, D});
                performCuts(piece, split);

                piece.set({A, H, G, E});
                split[1].set({B, H, G, F});
                performPaste(piece, split);

                h = piece.height();
            }
        }

        // We now cannot perform more mul/div by 2 operations
        // Shift by dh in at most 2 cuts
        ld dh = h - heights[i];
        Point A, B, C, D, E, F, G, H, S, T;
        A = piece.get(0);
        B = piece.get(1);
        C = piece.get(2);
        D = piece.get(3);
        S = (B - A).unit() * dh;
        E = A + S;
        F = C - S;
        G = B - S;
        ld pr = (E - A).dist() / (G - A).dist();
        T = (F - A).unit() * (F - A).dist() * pr;
        H = A + T;

        vector<Polygon> split(3);
        split[0].set({E, B, C, F, H});
        split[1].set({A, F, D});
        split[2].set({A, E, H});

        performCuts(piece, split);

        // Let D be stationary
        Point nA, nB, nC, Q;
        ld dw = (H - E).dist();
        Q = (A - D).unit() * dw;
        split[0].shift(Q - S);
        split[2].set({F+Q-S, C+Q-S, F});

        piece.set({A+Q, G+Q, F, D});

        performPaste(piece, split);

        res.push_back(piece);
    }

    return res;
}

Polygon combineRectangle(vector<Polygon> &pieces, ld height) {
    // Stack all pieces together
    Polygon res;
    ld width = 0;

    for (auto &piece : pieces) {
        ld w = piece.width();

        Point A(0, height), B(0, 0), C(w, 0), D(w, height), S(width, 0);
        piece.set({A+S, B+S, C+S, D+S});

        width += w;
    }

    Point A(0, height), B(0, 0), C(width, 0), D(width, height);
    res.set({A, B, C, D});

    performPaste(res, pieces);

    return res;
}

vector<Polygon> separateRectangle(Polygon rect, vector<Polygon> &pieces, ld height) {
    vector<Polygon> res;

    for (auto &piece : pieces) {
        ld area = piece.area();
        ld width = area / height;

        if (abs(rect.width() - width) < EPS) {
            res.push_back(rect);
            continue;
        }

        Point A, B, C, D, E, F, S;
        A = rect.get(0);
        B = rect.get(1);
        C = rect.get(2);
        D = rect.get(3);
        E = D - width;
        F = C - width;

        vector<Polygon> split(2);
        split[0].set({A, B, F, E});
        split[1].set({E, F, C, D});
        
        performCuts(rect, split);

        rect = split[0];
        res.push_back(split[1]);

        assert(abs(split[1].area() - area) < EPS);
    }

    return res;
}

pair<Point, ld> findTransformation(const Polygon &source, const Polygon &target) {
    assert(source.size() == target.size());
    
    Point s1 = source.poly[0], s2 = source.poly[1];
    Point t1 = target.poly[0], t2 = target.poly[1];
    
    Point translation = t1 - s1;
    
    Point sv = s2 - s1;
    Point tv = t2 - t1;
    ld angle = tv.angle() - sv.angle();
    
    return {translation, angle};
}

Point transformPoint(const Point &p, const Point &center, const Point &translation, ld angle) {
    Point translated = p - center;
    Point rotated = translated.rotate(angle);
    return rotated + center + translation;
}

vector<Polygon> composeTriangles(vector<Polygon> &pieces, vector<Polygon> &blueprint, vector<Polygon> &target) {
    vector<Polygon> res;

    assert(pieces.size() == blueprint.size());
    assert(pieces.size() == construct.size());
    assert(pieces.size() == decomp.size());

    int n = pieces.size();
    for (int i = 0; i < n; i++) {
        auto &piece = pieces[i];
        auto &blue = blueprint[i];
        
        auto [translation, angle] = findTransformation(blue, piece);
        Point center = blue.poly[0];
        
        vector<Polygon> relayed;
        for (auto &rpol : construct[i]) {
            Polygon transformed;
            transformed.id = rpol.id;
            
            for (int j = 0; j < rpol.size(); j++) {
                Point P = rpol.get(j);
                Point Q = transformPoint(P, center, translation, angle);
                transformed.add(Q);
            }
            
            relayed.push_back(transformed);
        }

        performCuts(piece, relayed);

        for (int j = 0; j < 4; j++) {
            decomp[i][j].id = relayed[j].id;
        }
        
        performPaste(target[i], decomp[i]);

        res.push_back(target[i]);
    }

    return res;
}

void solve() {
    Polygon S(0), T;
    int s; cin >> s; S.input(s);
    int t; cin >> t; T.input(t);

    ld h = sqrtl(T.area());

    vector<Polygon> U1 = decomposeTriangles(S, true);
    vector<Polygon> U2 = composeRectangles(U1, true);
    vector<ld> H1(U2.size(), h);
    vector<Polygon> U3 = normalizeRectangles(U2, H1, true);
    Polygon U4 = combineRectangle(U3, h);

    vector<Polygon> V1 = decomposeTriangles(T, false);
    vector<Polygon> V2 = composeRectangles(V1, false);
    vector<Polygon> V3 = separateRectangle(U4, V2, h);
    vector<ld> H2; for (auto &poly : V2) H2.push_back(poly.height());
    vector<Polygon> V4 = normalizeRectangles(V3, H2, true); 
    vector<Polygon> V5 = composeTriangles(V4, V2, V1);

    performPaste(T, V5);
}

signed main() {
    cin.tie(0);
    ios_base::sync_with_stdio(false);

    cout << fixed << setprecision(12);
    // cerr << fixed << setprecision(3);

    int t = 1;
    // cin >> t;
    while (t--)
        solve();
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...