[UVA10256]The Great Divide & EXT

壹、题目描述 ¶

传送门 to Luogu.

贰、题解 ¶

思路十分简易,判断蓝色凸包和红色凸包是否有交即可。

但是如何实现呢?我们的目的是,在两凸包中找到两个点,让他们形成的向量模长为 \(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)\).

posted @ 2021-08-07 22:09  Arextre  阅读(45)  评论(0编辑  收藏  举报