NC223799. TheGrandTournament
描述
输入描述
The input contains multiple cases. The first line of the input contains a single integer), indicating the number of test cases.
For each case, the first line of the input contains four integers![]()
![]()
, denoting the coordinates of the bottom-left and the top-right corners of the rectangle. Each of the next two lines contains four integers
, denoting a segment that connects
and
, where
and
.
For each case, it is guaranteed that the two endpoints of each segment do not coincide.
输出描述
For each test case, print a single line containing a single real number, the area of valid positions of the car. Your answer will be considered correct if the absolute or relative error does not exceed.
Formally, if your answer is a and the jury's answer is b, then your answer will be considered correct if and only if.
示例1
输入:
2 0 0 3 3 1 1 1 2 2 1 2 2 0 0 3 3 1 1 1 2 1 2 2 2
输出:
0.000000000000000 1.000000000000000
C++(g++ 7.5.0) 解法, 执行用时: 419ms, 内存消耗: 2592K, 提交时间: 2023-07-10 13:55:43
#include <cmath> #include <algorithm> #include <vector> #include <iostream> #include<vector> #include<stdio.h> #include<iomanip> /* 有时候eps反而要调大 判断直线和线段有没有交点,直接看两个toleft即可,如果先求直线和线段交点,再判断交点在不在线段上,精度会出问题 尽量用toleft等bool去判断,而不是算出来某个点的坐标再去对比 最好不要求角度,精度有问题。 math.h要用fabs,cmath可以用abs但必须要using namespace */ using namespace std; typedef double db; const db eps = 1e-9; int sign(db a) { return a < -eps ? -1 : a > eps; } int cmp(db a, db b) { return sign(a - b); } db min(db a,db b) { if(a<b) return a; return b; } //判断m是否介于a,b之间,不需要a<b bool db_ismiddle(db a, db m, db b) { return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m); } struct Point { db x, y; Point(db x = 0, db y = 0) : x(x), y(y) {} bool operator==(const Point &a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); } Point operator+(const Point &a) const { return {x + a.x, y + a.y}; }//okk Point operator-(const Point &a) const { return {x - a.x, y - a.y}; }//okk Point operator-() const { return {-x, -y}; } Point operator*(const db k) const { return {k * x, k * y}; }//okk Point operator/(const db k) const { return {x / k, y / k}; }//okk db operator*(const Point &a) const { return x * a.x + y * a.y; } // Dot okk db operator^(const Point &a) const { return x * a.y - y * a.x; } // Cross okk bool operator<(const Point &a) const { if (abs(x - a.x) <= eps) return y < a.y - eps; return x < a.x - eps; } //点是否介于点a,b之间,即在以a,b为对角线的矩形内,a,b可以交换顺序 bool ismiddle(const Point &a, const Point &b) const//okkk { return db_ismiddle(a.x, (*this).x, b.x) && db_ismiddle(a.y, (*this).y, b.y); } // 向量平行 bool is_par(const Point &a) const { return abs((*this) ^ a) <= eps; } //okk // 向量垂直 bool is_ver(const Point &a) const { return abs((*this) * a) <= eps; } // 判断向量a是否在这个向量的左侧 int toleft(const Point &a) const//okk { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); } //判断点不严格在线段上,在端点也算 int onseg(const Point &a,const Point &b){ return (b - a) ^ ((*this) - a) == 0 && ismiddle(a, b); } //单位向量 Point unit() const { return (*this) / len(); }//okk // 逆时针转90度 Point rot90() const { return Point(-y, x); } // 极角,[-pi,pi] db alpha() { return atan2(y, x); } // 向量模长平方 db len2() const { return (*this) * (*this); } // 两点距离平方 db dis2(const Point &a) const { return (a - (*this)).len2(); } // 向量模长 db len() const { return sqrt(len2()); } // 两点距离 db dis(const Point &a) const { return (a - (*this)).len(); } // 向量夹角 db ang(const Point &a) const { return acos(((*this) * a) / (this->len() * a.len())); } //看向量是否在x轴上方,极角在[0,pi)属于上面,[-pi,0)属于下面 int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); } //okkkk //点到直线ab的投影点,a!=b Point proj(const Point &a, const Point &b)const { Point dir = b - a; return a + dir * (dir * ((*this) - a) / dir.len2()); } //关于直线ab的对称点 Point sym(const Point &a,const Point &b)const { return (*this).proj(a, b) * 2 - (*this); } //点到线段的最短距离 db dis_to_seg(const Point &a, const Point &b) { if (a == b) return (*this).dis(a); Point h = (*this).proj(a, b); if ((*this).ismiddle(a, b)) return (*this).dis(h); return min((*this).dis(a), (*this).dis(b)); } // 向量逆时针旋转rad Point rot(const double rad) const { return {x * cos(rad) - y * sin(rad), x * sin(rad) + y * cos(rad)}; } void write() { std::cout << "(" << x << "," << y << ")" << '\n'; } }; //按o1点极角排序 // sort(p.begin(), p.end(), [&](const P &a, const P &b){ // int qa = (a-o1).quad(), qb = (b-o1).quad(); // if(qa!=qb) // return qa < qb; // else // return sign((a-o1) ^ (b-o1)) > 0; // }); struct Line { Point p, v; // p+tv Line(Point a, Point b) : p(a), v(b) {} bool operator==(const Line &a) const { return (v.is_par(a.v) && v.is_par(p - a.p)); } // 两直线平行 bool is_par(const Line &a) const { return (v.is_par(a.v) && !v.is_par(p - a.p)); } // 两直线垂直 bool is_ver(const Line &a) const { return v.is_ver(a.v); } // 点a是否在直线上 bool is_on(const Point &a) const { return v.is_par(a - p); } // 点a是否在直线左边 int toleft(const Point &a) const { return v.toleft(a - p); } // 两直线交点,要提前排除平行情况 Point inter(const Line &a) const { return p + v * ((a.v ^ (p - a.p)) / (v ^ a.v)); } // 点a到直线距离 db dis(const Point &a) const { return abs(v ^ (a - p)) / v.len(); } // 点a到直线投影 Point proj(const Point &a) const { return p + v * ((v * (a - p)) / (v * v)); } //判断直线和线段有没有交点,重合,交在端点也算 bool isinter_on_seg(const Point &a,const Point &b)const{ return (*this).toleft(a) * (*this).toleft(b) <= 0; // 去掉=就是交在线段内部 } /*bool operator<(const Line &a) const { if (abs(v^a.v)<=eps && v*a.v>=-eps) return ((a.p-p)^v)>eps; return argcmp(v,a.v); }*/ }; struct Seg{ Point a, b; Seg(Point _a,Point _b):a(_a),b(_b){} //线段是否相交 bool is_inter_seg(Point c,Point d){ Line l1(a, b - a); Line l2(c, d - c); if (l1.isinter_on_seg(c, d) && l2.isinter_on_seg(a, b)) return 1; return 0; } }; typedef Point P; typedef Line L; typedef P Vector; typedef Seg S; //多边形 //求简单多边形面积 db area(vector<Point>ps){//点按逆时针方向给,顺时针给的答案取个反就行 db ret = 0; for (int i = 0; i < ps.size();++i) ret += ps[i] ^ ps[(i + 1) % ps.size()]; return ret / 2; } db crossop(Point a,Point b,Point c){//okkkk return sign((b - a) ^ (c - b)); } //求凸包,自己选返回值,vector时逆时针返回下凸集,上凸集 //是严格凸包,求周长不影响,求点集有影响,abc三点共线不会把b算进去 //首先处理一下重合点 db convexHull(vector<Point>ps){//okkkkkk int n = ps.size(); if(n<=1) return 0; // return ps; sort(ps.begin(), ps.end()); vector<Point> qs(n * 2); int k = 0; for (int i = 0; i < n;qs[k++]=ps[i++]) while(k>1&&crossop(qs[k-2],qs[k-1],ps[i])<=0) --k; for (int i = n - 2, t = k; i >= 0;qs[k++]=ps[i--]) while(k>t&&crossop(qs[k-2],qs[k-1],ps[i])<=0) --k; qs.resize(k - 1); db res = 0; for (int i = 0; i < qs.size()-1;++i) res += qs[i].dis(qs[i + 1]); res += qs[qs.size() - 1].dis(qs[0]); return res; //return qs; // 如果返回点集,第一个点只存了一次 } //旋转卡壳求凸多边形直径,ps要求逆时针 db convexDiameter(vector<Point> ps){//okkkkk int n = ps.size(); if(n<=1) return 0; int is = 0, js = 0; for (int k = 1; k < n;++k) is = ps[k] < ps[is] ? k : is, js = ps[js] < ps[k] ? k : js; int i = is, j = js; db ret = ps[i].dis(ps[j]); do{ if (((ps[(i + 1) % n] - ps[i]) ^ (ps[(j + 1) % n] - ps[j]))>=0) (++j) %= n; else (++i) %= n; ret = max(ret, ps[i].dis(ps[j])); } while (i != is || j != js); return ret; } //直线切凸多边形,返回直线左边的多边形,ps要求逆时针顺序,直线方向向量为q1q2 vector<P>convexCut(const vector<P> ps,P q1,P q2){ vector<P> qs; int n = ps.size(); for (int i = 0; i < n;++i) { P p1 = ps[i], p2 = ps[(i + 1) % n]; int d1 = crossop(q1, q2, p1), d2 = crossop(q1, q2, p2); if(d1>=0) qs.push_back(p1); if(d1*d2<0) qs.push_back(L{p1, p1 - p2}.inter(L{q1, q1 - q2})); } return qs; } db calc(P a,P b,P c,vector<P>ps) { vector<P> qs; qs = convexCut(ps, a, a + (b - a).rot90()); qs = convexCut(qs, a, a + (c - a).rot90()); return abs(area(qs)); } void solve() { db xl, yl, xr, yr; scanf("%lf%lf%lf%lf", &xl, &yl, &xr, &yr); P p1, p2, p3, p4; scanf("%lf%lf%lf%lf", &p1.x, &p1.y, &p2.x, &p2.y); scanf("%lf%lf%lf%lf", &p3.x, &p3.y, &p4.x, &p4.y); if(!S{p1,p2}.is_inter_seg(p3,p4)) { printf("0.000000000000000\n"); return; } if((p1==p3&&p2==p4)||(p1==p4&&p2==p3)) { printf("%.15lf\n", (xr - xl) * (yr - yl)); return; } vector<P> rect = {P(xl, yl), P(xr, yl), P(xr, yr), P(xl, yr)}; vector<P> p; db ans = 0; if(Vector{p1-p2}.is_par(p3-p4)) { p = convexCut(rect, p1, p1 + (p1 - p2).rot90()); p = convexCut(p, p2, p2 + (p2 - p1).rot90()); p = convexCut(p, p3, p3 + (p3 - p4).rot90()); p = convexCut(p, p4, p4 + (p4 - p3).rot90()); ans+=area(p); } if(p1==p3) ans += calc(p1, p2, p4, rect); else if(p1==p4) ans += calc(p1, p2, p3, rect); else if(p2==p3) ans += calc(p2, p1, p4, rect); else if(p2==p4) ans += calc(p2, p1, p3, rect); printf("%.15lf\n", ans); } int main() { ios::sync_with_stdio(false); int t; scanf("%d", &t); while(t--) { solve(); } return 0; } /* 0 0 4 4 3 0 3 1 4 0 3 1 */
C++ 解法, 执行用时: 417ms, 内存消耗: 2316K, 提交时间: 2022-05-01 20:17:44
#include <bits/stdc++.h> #define fp(i,a,b) for(int i=a,__##i=(b)+1;i<__##i;++i) #define fd(i,a,b) for(int i=a,__##i=(b)-1;i>__##i;--i) #define X first #define Y second using namespace std; const int N=1e5+5; using arr=int[N]; using ll=long long; using db=long double; using pdd=pair<db,db>; const db eps=1e-12; inline void Read(pdd &p){ scanf("%Lf%Lf",&p.X,&p.Y); } inline void Print(pdd &p){ cout<<p.X<<','<<p.Y<<' '; } inline db Cross(const pdd &l,const pdd &r){ return l.X*r.Y-l.Y*r.X; } inline pdd operator -(const pdd &l,const pdd &r){ return {l.X-r.X,l.Y-r.Y}; } inline pdd operator +(const pdd &l,const pdd &r){ return {l.X+r.X,l.Y+r.Y}; } inline pdd operator *(const db l,const pdd &r){ return {l*r.X,l*r.Y}; } inline pdd Rot(const pdd p){ return {-p.Y,p.X}; } inline bool Equal(const pdd &l,const pdd &r){ return abs(l.X-r.X)<eps&&abs(l.Y-r.Y)<eps; } inline pdd Inter(pdd s1,pdd e1,pdd s2,pdd e2){ auto d=Cross(e1-s1,e2-s2); if(abs(d)<eps) return pdd(); auto p=Cross(e1-s2,e2-s2),q=Cross(e2-s2,s1-s2); return (1.0/d)*(p*s1+q*e1); } inline vector<pdd> Cut(const vector<pdd> &poly,pdd s,pdd e){ vector<pdd> ret; fp(i,0,(int)poly.size()-1){ pdd cur=poly[i],prev=i?poly[i-1]:poly.back(); bool side=Cross(e-s,cur-s)<-eps; if(side!=(Cross(e-s,prev-s)<-eps)) ret.push_back(Inter(s,e,cur,prev)); if(side) ret.push_back(cur); } return ret; } inline db Area(const vector<pdd> &poly){ db ret=0; fp(i,0,(int)poly.size()-1){ pdd u=poly[i],v=i?poly[i-1]:poly.back(); ret+=Cross(v,u); } return ret/2; } int n; inline db Solve(){ db xl,xr,yl,yr; scanf("%Lf%Lf%Lf%Lf",&xl,&yl,&xr,&yr); pdd P1(xl,yl),P2(xr,yl),P3(xr,yr),P4(xl,yr); vector<pdd> P{P1,P2,P3,P4}; pdd A1,A2,B1,B2; Read(A1), Read(A2), Read(B1), Read(B2); if(A1>A2) swap(A1,A2); if(B1>B2) swap(B1,B2); if(Equal(A1,B1)&&Equal(A2,B2)){ auto sz=P3-P1; return sz.X*sz.Y; } db ans=0; if(abs(Cross(A2-A1,B2-A1))<eps&&abs(Cross(A2-A1,B1-A1))<eps){ pdd L=max(A1,B1),R=min(A2,B2); if(L<R){ auto v=Cut(P,L,L+Rot(R-L)); v=Cut(v,R,R+Rot(L-R)); ans+=Area(v); } } if(Equal(A1,B2)) swap(B1,B2); else if(Equal(A2,B1)) swap(A1,A2); else if(Equal(A2,B2)) swap(A1,A2),swap(B1,B2); if(Equal(A1,B1)){ auto v=Cut(P,A1,A1+Rot(A1-A2)); v=Cut(v,B1,B1+Rot(B1-B2)); ans+=Area(v); } return ans; } int main() { int T; scanf("%d",&T); while(T--) printf("%.12Lf\n",Solve()); return 0; }