Submission #198124

#TimeUsernameProblemLanguageResultExecution timeMemory
198124model_codeScissors and Tape (CEOI19_scissors)C++17
100 / 100
13 ms448 KiB
#include <algorithm> #include <cassert> #include <complex> #include <iomanip> #include <iostream> #include <vector> using namespace std; // universal geometric functions typedef double rational; typedef complex<rational> point; typedef vector<point> poly; const rational EPSILON = 1e-9; bool is_negative(rational x) { return x < -EPSILON; } bool is_zero(rational x) { return abs(x) <= EPSILON; } bool is_positive(rational x) { return x > EPSILON; } bool are_equal(rational x, rational y) { if (is_zero(x-y)) return true; rational lo = min( x*(1-EPSILON), x*(1+EPSILON) ), hi = max( x*(1-EPSILON), x*(1+EPSILON) ); return (lo <= y && y <= hi); } bool are_equal(const vector<rational> &A, const vector<rational> &B) { if (A.size() != B.size()) return false; for (unsigned n=0; n<A.size(); ++n) if (!are_equal(A[n],B[n])) return false; return true; } bool are_equal(const point &A, const point &B) { return is_zero(real(B)-real(A)) && is_zero(imag(B)-imag(A)); } int signum(rational x) { if (is_zero(x)) return 0; if (is_negative(x)) return -1; return 1; } rational square_size (const point &A) { return real(A) * real(A) + imag(A) * imag(A); } rational dot_product (const point &A, const point &B) { return real(A) * real(B) + imag(A) * imag(B); } rational cross_product(const point &A, const point &B) { return real(A) * imag(B) - real(B) * imag(A); } rational size(const point &A) { return sqrt(real(A) * real(A) + imag(A) * imag(A)); } void normalize(point &A) { rational Asize = size(A); A *= (1/Asize); } bool colinear(const point &A, const point &B, const point &C) { return is_zero( cross_product( B-A, C-A )); } bool colinear(const point &B, const point &C) { return is_zero( cross_product( B, C )); } bool clockwise(const point &A, const point &B, const point &C) { return is_negative( cross_product( B-A, C-A )); } bool clockwise(const point &B, const point &C) { return is_negative( cross_product( B, C )); } bool counterclockwise(const point &A, const point &B, const point &C) { return is_positive( cross_product( B-A, C-A )); } bool counterclockwise(const point &B, const point &C) { return is_positive( cross_product( B, C )); } rational poly_area(const poly &V) { rational res = 0; for (unsigned i=0; i<V.size(); i++) res += cross_product( V[i], V[(i+1)%V.size()] ); return abs(0.5 * res); } vector<rational> side_lengths(const poly &P) { int N = P.size(); vector<rational> answer; for (int n=0; n<N; ++n) answer.push_back( size( P[(n+1)%N] - P[n] ) ); return answer; } // task-specific functions int NEXT_ID; struct object { int ID; poly P; object(const poly &P) : P(P) { ID = NEXT_ID++; } }; void initialize() { NEXT_ID = 0; cout << fixed << setprecision(15); cerr << fixed << setprecision(15); } void print_poly(const poly &P) { cout << P.size(); for (auto pt : P) cout << " " << real(pt) << " " << imag(pt); cout << endl; } poly load_poly() { int N; cin >> N; poly P(N); rational x, y; for (int n=0; n<N; ++n) { cin >> x >> y; P[n] = point(x,y); } return P; } poly nice_rectangle(rational x, rational y, rational dx = 0, rational dy = 0) { return { point(dx,dy), point(dx+x,dy), point(dx+x,dy+y), point(dx,dy+y) }; } void cut_and_paste(int startWith, const vector<poly> &cutInto, const vector<poly> &rearrangeInto, const poly &endWith) { cout << "scissors" << endl << startWith << " " << cutInto.size() << endl; vector<int> new_ids; for (const auto &piece : cutInto) { new_ids.push_back( NEXT_ID++ ); print_poly(piece); } cout << "tape" << endl << rearrangeInto.size(); for (int x : new_ids) cout << " " << x; cout << endl; for (const auto &piece : rearrangeInto) print_poly(piece); print_poly(endWith); } object nice_rectangle_to_nice_rectangle(object Rin, const poly &Rout) { while (true) { rational x1 = real(Rin.P[2]), y1 = imag(Rin.P[2]), x2 = real(Rout[2]), y2 = imag(Rout[2]); if (are_equal(x1,x2)) return Rin; if (!is_negative(x1 - 2*x2)) { // x1 is at least twice as big, fold in half cut_and_paste( Rin.ID, { nice_rectangle( x1/2, y1 ), nice_rectangle( x1/2, y1, x1/2, 0 ) }, { nice_rectangle( x1/2, y1 ), nice_rectangle( x1/2, y1, 0, y1 ) }, nice_rectangle( x1/2,y1*2 ) ); Rin = object(nice_rectangle(x1/2,y1*2)); continue; } if (!is_negative(y1 - 2*y2)) { // y1 is at least twice as big, fold in half cut_and_paste( Rin.ID, { nice_rectangle( x1, y1/2 ), nice_rectangle( x1, y1/2, 0, y1/2 ) }, { nice_rectangle( x1, y1/2 ), nice_rectangle( x1, y1/2, x1, 0 ) }, nice_rectangle( x1*2,y1/2 ) ); Rin = object(nice_rectangle(x1*2,y1/2)); continue; } if (is_positive(x1 - x2)) { // x1 is at most twice as big, cut into three and reassemble rational offx = x1 - x2, offy = y1 * offx / x2; cut_and_paste( Rin.ID, { { point(0,0), point(x2,y1), point(0,y1) }, { point(0,0), point(offx,0), point(offx,offy) }, { point(offx,0), point(x1,0), point(x1,y1), point(x2,y1), point(offx,offy) } }, { { point(0,offy), point(x2,y2), point(0,y2) }, { point(x2-offx,y1), point(x2,y1), point(x2,y2) }, { point(0,0), point(x2,0), point(x2,y1), point(x2-offx,y1), point(0,offy) } }, nice_rectangle(x2,y2) ); return object(Rout); } else { // y1 is at most twice as big, cut into three and reassemble rational offy = y1 - y2, offx = x1 * offy / y2; cut_and_paste( Rin.ID, { { point(0,0), point(x1,0), point(x1,y2) }, { point(0,0), point(offx,offy), point(0,offy) }, { point(0,offy), point(offx,offy), point(x1,y2), point(x1,y1), point(0,y1) } }, { { point(offx,0), point(x2,0), point(x2,y2) }, { point(x2-offx,y2-offy), point(x2,y2), point(x2-offx,y2) }, { point(0,0), point(offx,0), point(x1,y2-offy), point(x1,y2), point(0,y2) } }, nice_rectangle(x2,y2) ); return object(Rout); } } } pair<poly,poly> triangle_to_nice_triangle(const poly &P) { vector<double> sides = side_lengths(P); vector<double> tmp = sides; int offset = 0; rotate( tmp.begin(), tmp.begin()+1, tmp.end() ); if (tmp > sides) sides = tmp, offset = 2; rotate( tmp.begin(), tmp.begin()+1, tmp.end() ); if (tmp > sides) sides = tmp, offset = 1; rational x = sides[0], y = sides[1], z = sides[2], cx = (x*x + z*z - y*y) / (2*x), cy = sqrt( z*z - cx*cx ); poly target{ point(0,0), point(x,0), point(cx,cy) }; poly source{ target[offset], target[(offset+1)%3], target[(offset+2)%3] }; return {source, target}; } object triangle_to_nice_triangle(const object &Tin) { auto nice = triangle_to_nice_triangle(Tin.P); cout << "tape" << endl; cout << "1 " << Tin.ID << endl; print_poly(nice.first); print_poly(nice.second); return object(nice.second); } object nice_triangle_to_nice_rectangle(const object &Tin) { rational x = real(Tin.P[1]), cx = real(Tin.P[2]), cy = imag(Tin.P[2]); cut_and_paste( Tin.ID, { { point(0,0), point(x,0), point((x+cx)/2,cy/2), point(cx/2,cy/2) }, { point(cx,cy/2), point((x+cx)/2,cy/2), point(cx,cy) }, { point(cx/2,cy/2), point(cx,cy/2), point(cx,cy) } }, { { point(0,0), point(x,0), point((x+cx)/2,cy/2), point(cx/2,cy/2) }, { point(x,cy/2), point((x+cx)/2,cy/2), point(x,0) }, { point(cx/2,cy/2), point(0,cy/2), point(0,0) } }, nice_rectangle(x,cy/2) ); return object(nice_rectangle(x,cy/2)); } object rectangle_to_nice_triangle(const object &Rin, const poly &tri) { poly nice = triangle_to_nice_triangle(tri).second; rational x = real(nice[1]), cx = real(nice[2]), cy = imag(nice[2]); // normalize the rectangle cout << "tape" << endl; cout << "1 " << Rin.ID << endl; rational width = real(Rin.P[2]) - real(Rin.P[0]), height = imag(Rin.P[2]) - imag(Rin.P[0]); print_poly( nice_rectangle(width,height) ); print_poly( nice_rectangle(width,height) ); object rect(nice_rectangle(width,height)); // resize the rectangle rect = nice_rectangle_to_nice_rectangle(rect,nice_rectangle(x,width*height/x)); // cut the rectangle and assemble the triangle cut_and_paste( rect.ID, { { point(0,0), point(x,0), point((x+cx)/2,cy/2), point(cx/2,cy/2) }, { point(x,cy/2), point((x+cx)/2,cy/2), point(x,0) }, { point(cx/2,cy/2), point(0,cy/2), point(0,0) } }, { { point(0,0), point(x,0), point((x+cx)/2,cy/2), point(cx/2,cy/2) }, { point(cx,cy/2), point((x+cx)/2,cy/2), point(cx,cy) }, { point(cx/2,cy/2), point(cx,cy/2), point(cx,cy) } }, nice ); return object(nice); } bool is_triangle_ear(const poly &P, point A, point B, point C) { if (!counterclockwise(A,B,C)) return false; for (point X : P) { if (are_equal(A,X) || are_equal(B,X) || are_equal(C,X)) continue; if (counterclockwise(A,B,X) && counterclockwise(B,C,X) && counterclockwise(C,A,X)) return false; } return true; } vector<poly> triangulate(poly P) { int N = P.size(); if (N == 3) return {P}; for (int n=0; n<N; ++n) if (is_triangle_ear(P,P[n],P[(n+1)%N],P[(n+2)%N])) { poly Q = P; Q.erase( Q.begin()+(n+1)%N ); vector<poly> answer = triangulate(Q); answer.push_back( {P[n],P[(n+1)%N],P[(n+2)%N]} ); return answer; } assert(false); } vector<object> triangulate_input(const object &Pin) { int N = Pin.P.size(); cout << "scissors" << endl; cout << Pin.ID << " " << N-2 << endl; vector<object> answer; for (poly troj : triangulate(Pin.P)) { print_poly(troj); answer.push_back(object(troj)); } return answer; } object merge_rectangulation(const vector<object> &RECT) { cout << "tape" << endl; cout << RECT.size(); for (auto rect : RECT) cout << " " << rect.ID; cout << endl; rational dx = 0; for (auto rect : RECT) { auto P = rect.P; rational width = real(P[2]); for (auto &pt : P) pt += dx; print_poly(P); dx += width; } print_poly(nice_rectangle(dx,dx)); return object(nice_rectangle(dx,dx)); } vector<object> split_square(const object &Sin, const vector<poly> &pieces) { rational a = real(Sin.P[2]), dx = 0; cout << "scissors" << endl; cout << Sin.ID << " " << pieces.size() << endl; vector<object> answer; for (auto tri : pieces) { rational width = poly_area(tri) / a; auto rect = nice_rectangle(width,a,dx,0); print_poly(rect); answer.push_back(object(rect)); dx += width; } return answer; } void merge_triangulation(const vector<object> &have, vector<poly> &want, poly &goal) { int N = have.size(); cout << "tape" << endl; cout << N; for (auto tri : have) cout << " " << tri.ID; cout << endl; for (int n=0; n<N; ++n) { auto havelen = side_lengths(have[n].P); while (true) { auto wantlen = side_lengths(want[n]); if (are_equal(havelen,wantlen)) break; rotate( want[n].begin(), want[n].begin()+1, want[n].end() ); } print_poly(want[n]); } print_poly(goal); } int main() { initialize(); poly start = load_poly(); poly goal = load_poly(); object START(start); vector<object> TRIANGULATION = triangulate_input(START); rational area = poly_area(start); rational desired_y = sqrt(area); vector<object> RECTANGULATION; for (auto tri : TRIANGULATION) { object nicetri = triangle_to_nice_triangle(tri); object rect = nice_triangle_to_nice_rectangle(nicetri); rational rect_area = poly_area(rect.P); object rect2 = nice_rectangle_to_nice_rectangle(rect,nice_rectangle(rect_area/desired_y,desired_y)); RECTANGULATION.push_back(rect2); } object square = merge_rectangulation(RECTANGULATION); vector<poly> output_triangulation = triangulate(goal); RECTANGULATION = split_square(square,output_triangulation); TRIANGULATION.clear(); for (unsigned n=0; n<output_triangulation.size(); ++n) TRIANGULATION.push_back(rectangle_to_nice_triangle(RECTANGULATION[n],output_triangulation[n])); merge_triangulation(TRIANGULATION,output_triangulation,goal); }
#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...