[UVA10256]The Great Divide & EXT
壹、题目描述 ¶
贰、题解 ¶
思路十分简易,判断蓝色凸包和红色凸包是否有交即可。
但是如何实现呢?我们的目的是,在两凸包中找到两个点,让他们形成的向量模长为 \(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)\).