[UVA10256]The Great Divide & EXT
阿新 • • 發佈:2021-08-08
壹、題目描述 ¶
貳、題解 ¶
思路十分簡易,判斷藍色凸包和紅色凸包是否有交即可。
但是如何實現呢?我們的目的是,在兩凸包中找到兩個點,讓他們形成的向量模長為 \(0\),於是我們考慮閔科夫斯基和,它原本的定義是:
\[A+B=\left\{a+b|a\in A,b\in B\right\} \]但是我們要讓它相減,於是我們可以讓其中一個凸包作中心對稱,然後再求閔可夫斯基和,得到的凸包,檢查 \((0,0)\) 是否在凸包中即可。
注意程式碼實現時,凸包退化為點或線段的情形。
叄、參考程式碼 ¶
#include<cstdio> #include<algorithm> #include<vector> #include<cmath> #include<cstdlib> #include<ctime> #include<set> using namespace std; // #define NDEBUG #include<cassert> namespace Elaina{ #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i) #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i) #define fi first #define se second #define mp(a, b) make_pair(a, b) #define Endl putchar('\n') #define mmset(a, b) memset(a, b, sizeof a) // #define int long long typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<class T>inline T fab(T x){ return x<0? -x: x; } template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); } template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); } template<class T>inline T readin(T x){ x=0; int f=0; char c; while((c=getchar())<'0' || '9'<c) if(c=='-') f=1; for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48)); return f? -x: x; } template<class T>inline void writc(T x, char s='\n'){ static int fwri_sta[1005], fwri_ed=0; if(x<0) putchar('-'), x=-x; do fwri_sta[++fwri_ed]=x%10, x/=10; while(x); while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed); putchar(s); } } using namespace Elaina; namespace Geometry{ /** <---------------------- Basic function ----------------------> */ const double eps=1e-8; const double Pi=acos(-1.0); const int INSIDE_CHECK_DATA=5; const int STACK_SIZE=1e5; inline int comper(const double x, const double y){ if(fab(x-y)<eps) return 0; return x>y? 1: -1; } /** <---------------------- Vector & Point ----------------------> */ struct Vec2d{ double x, y; Vec2d(){} Vec2d(double X, double Y): x(X), y(Y){} inline Vec2d operator -(const Vec2d rhs) const{ return Vec2d(x-rhs.x, y-rhs.y); } inline Vec2d operator +(const Vec2d rhs) const{ return Vec2d(x+rhs.x, y+rhs.y); } // dot multiplication inline double operator ^(const Vec2d rhs) const{ return x*rhs.x+y*rhs.y; } // cross multiplication inline double operator *(const Vec2d rhs) const{ return x*rhs.y-y*rhs.x; } inline Vec2d operator *(const double k) const{ return Vec2d(k*x, k*y); } inline Vec2d rotate(const double theta) const{ return Vec2d(x*cos(theta)-y*sin(theta), x*sin(theta)+y*cos(theta)); } inline double norm() const{ return sqrt((*this)^(*this)); } inline friend double norm(const Vec2d v){ return v.norm(); } inline void print() const{ printf("(%.5f, %.5f)\n", x, y); } inline friend void print(const Vec2d v){ v.print(); } }; struct Point{ double x, y; Point(){} Point(double X, double Y): x(X), y(Y){} inline Point operator +(const Vec2d v) const{ return Point(x+v.x, y+v.y); } inline Vec2d operator -(const Point rhs) const{ return Vec2d(x-rhs.x, y-rhs.y); } inline void print() const{ printf("(%.5f, %.5f)\n", x, y); } inline Vec2d ptov() const{ return Vec2d(x, y); } }; inline Point getMid(const Point a, const Point b){ return Point((a.x+b.x)/2, (a.y+b.y)/2); } inline Vec2d ptov(const Point p){ return Vec2d(p.x, p.y); } /** get the vertical vector */ inline Vec2d getVertical(const Vec2d v){ return Vec2d(v.y, -v.x); } inline Point vtop(const Vec2d v){ return Point(v.x, v.y); } inline bool same(const Point p1, const Point p2){ return comper(p1.x, p2.x)==0 && comper(p1.y, p2.y)==0; } /** <---------------------- Line & Segment ----------------------> */ struct Line{ Point p; Vec2d v; Line(){} Line(Point P, Vec2d V): p(P), v(V){} inline void print(){ printf("point : "); p.print(); printf("vecto : "); v.print(); } }; struct Segmt{ Point p0, p1; Segmt(){} Segmt(const Point P0, const Point P1): p0(P0), p1(P1){} inline Point& operator [](const int i){ assert(i==0 || i==1); return i==0? p0: p1; } inline Line SegtoLine(){ return Line(p0, p1-p0); } inline void print(){ p0.print(); p1.print(); } }; inline bool parallel(Segmt a, Segmt b){ double c=(a[1]-a[0])*(b[1]-b[0]); if(comper(c, 0)==0) return 1; return 0; } inline bool parallel(Line a, Line b){ double c=a.v*b.v; if(comper(c, 0)==0) return 1; return 0; } inline double getDis(const Line l, const Point p){ Vec2d v=p-l.p; double siz=fab(v*l.v); return siz/l.v.norm(); } inline double getDis(Segmt l, const Point p){ double d0=(p-l[0])^(l[1]-l[0]), d1=(p-l[1])^(l[0]-l[1]); if(comper(d0, 0)>=0 && comper(d1, 0)>=0) return getDis(Line(l[0], l[1]-l[0]), p); return comper(d0, 0)<0? norm(p-l[0]): norm(p-l[1]); } inline bool isOnLine(const Line l, const Point p){ double dis=getDis(l, p); return comper(dis, 0)==0; } inline bool isOnSeg(const Segmt l, const Point p){ return comper(getDis(l, p), 0)==0; } inline bool segSegIntersect(Segmt a, Segmt b){ for(int i=0; i<2; ++i) if(isOnSeg(b, a[i])) return 1; for(int j=0; j<2; ++j) if(isOnSeg(a, b[j])) return 1; if(parallel(a, b)) return 0; double c0=(a[1]-a[0])*(b[0]-a[0]), c1=(a[1]-a[0])*(b[1]-a[0]); double c2=(b[1]-b[0])*(a[0]-b[0]), c3=(b[1]-b[0])*(a[1]-b[0]); if(comper(c0, 0)*comper(c1, 0)>0 || comper(c2, 0)*comper(c3, 0)>0) return 0; return 1; } inline bool segLineIntersect(Segmt s, const Line l){ if(isOnLine(l, s[0]) || isOnLine(l, s[1])) return 1; double c0=l.v*(s[0]-l.p), c1=l.v*(s[1]-l.p); if(comper(c0, 0)*comper(c1, 0)<0) return 1; return 0; } inline bool coincident(Line a, Line b){ if(!parallel(a, b)) return 0; if(comper(getDis(b, a.p), 0)==0) return 1; return 0; } /** @warning @p a and @p b shouldn't be the same Line or parallel */ inline Point getIntersect(Line a, Line b){ double d0=a.v*b.v; assert(comper(d0, 0)!=0); double t=b.v*(a.p-b.p)/d0; return a.p+a.v*t; } inline Line getVerticalLine(Segmt s){ return Line(getMid(s[0], s[1]), getVertical(s[0]-s[1])); } /** <---------------------- polygon & convex ----------------------> */ inline double getArea(vector<Point>poly){ int n=poly.size(); double s=0; for(int i=0; i<n; ++i) s+=poly[i].ptov()*poly[(i+1)%n].ptov(); return s*0.5; } // whether vector @p x is in the middle of @p a and @p b inline bool isInMiddle(Vec2d a, Vec2d b, Vec2d x){ if(comper(a*b, 0)<0) swap(a, b); return comper(a*x, 0)>=0 && comper(b*x, 0)<=0; } inline bool isInside(vector<Point>poly, Point p){ int n=poly.size(); rep(_, 1, INSIDE_CHECK_DATA){ double angle=(rand()/(double(RAND_MAX))-0.5)*2*Pi; Vec2d v(cos(angle), sin(angle)); bool res=0; for(int i=0; i<n; ++i){ if(isOnSeg(Segmt(poly[i], poly[(i+1)%n]), p)) return 1; if(isInMiddle(poly[i]-p, poly[(i+1)%n]-p, v)) res=!res; } if(res) return 1; } return 0; } Point sta[STACK_SIZE+5]; int ed; inline vector<Point> Graham(vector<Point>poly){ Point pivot; int k=0, n=poly.size(); if(n==1) return {poly[0]}; for(int i=1; i<n; ++i) if(comper(poly[i].y, poly[k].y)<0 || (comper(poly[i].y, poly[k].y)==0 && comper(poly[i].x, poly[k].x)<0)) k=i; pivot=poly[k]; swap(poly[0], poly[k]); auto cmp=[&](const Point a, const Point b){ Vec2d v1=a-pivot, v2=b-pivot; double c=v1*v2; return comper(c, 0)>0 || (comper(c, 0)==0 && comper(norm(v1), norm(v2))<0); }; sort(poly.begin()+1, poly.end(), cmp); sta[0]=poly[0], sta[1]=poly[1], ed=1; for(int i=2; i<n; ++i){ while(ed && comper((sta[ed]-sta[ed-1])*(poly[i]-sta[ed]), 0)<=0) --ed; sta[++ed]=poly[i]; } vector<Point>convex; for(int i=0; i<=ed; ++i) convex.push_back(sta[i]); return convex; } inline Point getCG(vector<Point>poly){ double area=0; int n=poly.size(); Point ret(0, 0); for(int i=0; i<n; ++i){ double t=poly[i].ptov()*poly[(i+1)%n].ptov(); area+=t; ret.x+=(poly[i].x+poly[(i+1)%n].x)*t; ret.y+=(poly[i].y+poly[(i+1)%n].y)*t; } ret.x/=3, ret.x/=area; ret.y/=3, ret.y/=area; return ret; } inline vector<Point> Minkowski_sum(vector<Point>p1, vector<Point>p2){ vector<Point>ret; int n1=p1.size(), n2=p2.size(); int i=0, j=0; while(i<n1 && j<n2){ Vec2d a=p1[(i+1)%n1]-p1[i], b=p2[(j+1)%n2]-p2[j]; ret.push_back(vtop(p1[i].ptov()+p2[j].ptov())); if(comper(a*b, 0)==0) ++i, ++j; else if(comper(a*b, 0)<0) ++j; else ++i; } while(i<n1) ret.push_back(vtop(p1[i++].ptov()+p2[0].ptov())); while(j<n2) ret.push_back(vtop(p1[0].ptov()+p2[j++].ptov())); return ret; } /** <---------------------- others ----------------------> */ struct Circle{ Point c; double r; Circle(){} Circle(const Point C, const double R): c(C), r(R){} inline double area(const double para=1){ return Pi*r*r*para; } inline Point& operator [](const int i){ assert(i==0); return c; } }; inline double getArea(Circle C, const double para=1){ return C.area(para); } inline Circle getCC(const Point a, const Point b, const Point c){ if(comper((b-a)*(c-b), 0)==0){ double dis=0; Point x, y; if(dis<norm(a-b)) dis=norm(a-b), x=a, y=b; if(dis<norm(a-c)) dis=norm(a-c), x=a, y=c; if(dis<norm(b-c)) dis=norm(b-c), x=b, y=c; return Circle(getMid(x, y), dis*0.5); } Line l1=getVerticalLine(Segmt(a, b)), l2=getVerticalLine(Segmt(c, b)); Point p=getIntersect(l1, l2); return Circle(p, norm(a-p)); } /** @brief if the node is on the edge, return 0*/ inline int isInside(Circle C, Point p){ int res=comper(norm(C[0]-p), C.r); if(res<0) return 1; if(res==0) return 0; return -1; } inline Circle getMinCirCover(vector<Point>p){ random_shuffle(p.begin(), p.end()); int n=p.size(); Circle C(p[0], 0); for(int i=1; i<n; ++i){ if(isInside(C, p[i])<0){ C=Circle(p[i], 0); for(int j=0; j<i; ++j) if(isInside(C, p[j])<0){ C[0]=getMid(p[i], p[j]), C.r=norm(p[i]-C[0]); for(int k=1; k<j; ++k) if(isInside(C, p[k])<0) C=getCC(p[i], p[j], p[k]); } } } return C; } inline bool isContain(Circle in, Circle out){ if(comper(out.r-norm(out[0]-in[0]), in.r)>=0) return 1; return 0; } inline double crossArea(Circle C1, Circle C2){ if(isContain(C1, C2) || isContain(C2, C1)) return min(C1.area(), C2.area()); else if(comper(norm(C1[0]-C2[0]), C1.r+C2.r)>=0) return 0; double d=norm(C1[0]-C2[0]); double alpha=acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*C1.r*d)); double beta=acos((C2.r*C2.r+d*d-C1.r*C1.r)/(2*C2.r*d)); return C1.area(alpha/Pi)+C2.area(beta/Pi)-C1.r*d*sin(alpha); } } using namespace Geometry; int n, m; vector<Point>red, blue, Rcon, Bcon, con; inline void input(){ Point p; red.clear(), blue.clear(); for(int i=1; i<=n; ++i){ scanf("%lf %lf", &p.x, &p.y); red.push_back(p); } for(int i=1; i<=m; ++i){ scanf("%lf %lf", &p.x, &p.y); blue.push_back(p); } } inline void solve(){ input(); for(Point& i: blue) i.x=-i.x, i.y=-i.y; Rcon=Graham(red), Bcon=Graham(blue); con=Minkowski_sum(Rcon, Bcon); int siz=con.size(); if(isInside(con, Point(0, 0))) printf("No\n"); else printf("Yes\n"); } signed main(){ // freopen("data.in", "r", stdin); while(1){ n=readin(1), m=readin(1); if(n==0) break; solve(); } return 0; }
肆、其他的東西 ¶
這裡有一道加強版:[JSOI2018]戰爭。
如果你對閔可夫斯基和足夠了解,你就知道這道題是讓你判斷一個點是否在倆凸包的和中(當然,其中一個要中心對稱),至於這個,你可以用極角座標排序+lower_bound()
+叉積解決。
還有另一個問題:
判斷兩凸包距離。
將一個凸包中心對稱之後,看原點到凸包和的距離,你可以 \(\mathcal O(n)\),也可以 \(\mathcal O(1)\).