cpp1const double pi=acos(-1);
cpp1const double eps=1e-8;
一般取 1e-8,再小可能就出问题了
cpp1inline int sgn(double x){return fabs(x)<=eps?0:(x>0?1:-1);}
当输出浮点数的 0 时,可能会出现输出了 -0.00000...
的情况,某些题目中需要特判:
cpp1inline double fit(double x){return sgn(x)==0?0:x;}
cpp12345struct Point{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
};
cpp1inline double dis(Point a,Point b){return hypot(a.x-b.x,a.y-b.y);}
cpp1bool operator==(Point b){return sgn(x-b.x)==0&&sgn(y-b.y)==0;}
可以直接用点来表示向量。把所有向量视为起点为原点
cpp1typedef Point Vector;
cpp1234Point operator+(Point b){return Point(x+b.x,y+b.y);}
Point operator-(Point b){return Point(x-b.x,y-b.y);}
Point operator*(double k){return Point(x*k,y*k);}
Point operator/(double b){return Point(x/k,y/k);}
cpp1double operator*(Point b){return x*b.x+y*b.y;}
cpp1inline double len(Point a){return sqrt(a*a);}
有时开方会损失精度,可以改为求长度的平方
cpp1inline double len2(Point a){return a*a;}
返回的是 cos 值
cpp1inline double angle(Point a,Point b){return a*b/len(a)/len(b);}
cpp1double operator^(Point b){return x*b.y-y*b.x;}
以 a 为 公共点,b 到 c 的有向面积
cpp1inline double area(Point a,Point b,Point c){return (b-a)^(c-a);}
向量 绕原点逆时针旋转度数 ,得到向量 ,有如下关系:
cpp1inline Point rotate(Point a,double rad){return Point(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}
法向量为向量逆时针旋转
cpp1inline Point normal(Point a){return Point(-a.y/len(a),a.x/len(a));}
cpp12345struct Line{
Point s,t;
Line(){}
Line(Point s,Point t):s(s),t(t){}
};
cpp1inline double slope(Point a,Point b){return (a.y-b.y)/(a.x-b.x);}
cpp123456inline double angle(Line l){
double k=atan2(l.s.y-l.t.y,l.s.x-l.t.x);
if(sgn(k)<0) k+=pi;
if(sgn(k-pi)==0) k=pi;
return k;
}
点在直线左边还是右边需要看你是从直线哪个方向观察的,这里传递的线段应视为有向线段,从 s 指向 t
cpp123456inline int plr(Point p,Line l){//0:on -1:left 1:right
int k=sgn((l.t-l.s)^(p-l.s));
if(k>0) return -1;
if(k<0) return 1;
return 0;
}
先用叉积判断是否共线,再用点积判断是否在线段上
cpp1inline bool on_seg(Point p,Line l){return plr(p,l)==0&&sgn((p-l.s)*(p-l.t))<=0;}
先叉积求出构成的平行四边形面积,然后面积除以底就得到了高,即点到直线距离
cpp1inline double dis(Point p,Line l){return fabs((p-l.s)^(l.s-l.t)/dis(l.s,l.t));}
cpp1inline Point proj(Point p,Line l){return l.s+(l.t-l.s)*(l.t-l.s)*(p-l.s)/len2(l.t-l.s);}
cpp1inline Point symmetry(Point p,Line l){return 2*proj(p,l)-p;}
过点向线段所在直线作垂线,如果垂足落在线段上,那么点到线段的距离为点到直线距离,反之,则为点到线段两端点的最小值
cpp1234inline double dis_seg(Point p,Line l){
if(sgn((p-l.s)*(l.t-l.s))<0||sgn((p-l.t)*(l.s-l.t))<0) return min(dis(p,l.s),dis(p,l.t));
return dis(p,l);
}
平行、重合、相交
cpp1234567inline int llr(Line l1,Line l2){//0:平行 1:相交 2:重合
if(sgn((l1.s-l1.t)^(l2.s-l2.t))==0){
if(plr(l1.s,l2)==0) return 2;
else return 0;
}
return 1;
}
返回值如下:
cpp123456inline int ssr(Line l1,Line l2){//0:不相交 1:非规范相交 2:规范相交
int c1=plr(l1.s,l2),c2=plr(l1.t,l2);
int d1=plr(l2.s,l1),d2=plr(l2.t,l1);
if(c1*c2<0&&d1*d2<0) return 2;
return (c1==0&&on_seg(l1.s,l2))||(c2==0&&on_seg(l1.t,l2))||(d1==0&&on_seg(l2.s,l1))||(d2==0&&on_seg(l2.t,l1));
}
返回值:
不相交
非规范相交
规范相交
重合
cpp1234567inline int slr(Line s,Line l){//0:不相交 1:非规范相交 2:规范相交 3:重合
int d1=plr(s.s,l),d2=plr(s.t,l);
if(d1==0&&d2==0) return 3;
if(d1*d2==0) return 1;
if(d1*d2<0) return 2;
return 0;
}
调用该函数前应确保两条直线存在交点
cpp12345inline Point cp(Line l1,Line l2){
double s1=(l1.t-l1.s)^(l2.s-l1.s);
double s2=(l1.t-l1.s)^(l2.t-l1.s);
return (l2.s*s2-l2.t*s1)/(s2-s1);
}
设 和 构成线段 , 和 构成线段 ,则 和 的距离为:
cpp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374const double pi=acos(-1);
const double eps=1e-8;
inline int sgn(double x){return fabs(x)<=eps?0:(x>0?1:-1);}
inline double fit(double x){return sgn(x)==0?0:x;}
struct Point{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
Point operator+(const Point& b) const {return Point(x + b.x, y + b.y);}
Point operator-(const Point& b) const {return Point(x - b.x, y - b.y);}
Point operator*(double k) const {return Point(x * k, y * k);}
double operator*(const Point& b) const {return x * b.x + y * b.y;}
Point operator/(double k) const {return Point(x / k, y / k);}
double operator^(const Point& b) const {return x * b.y - y * b.x;}
};
typedef Point Vector;
struct Line{
Point s,t;
Line(){}
Line(Point s,Point t):s(s),t(t){}
};
inline double dis(Point a,Point b){return hypot(a.x-b.x,a.y-b.y);}
inline double dis(Point p,Line l){return fabs((p-l.s)^(l.s-l.t)/dis(l.s,l.t));}
inline double len(Point a){return sqrt(a*a);}
inline double len2(Point a){return a*a;}
inline double slope(Point a,Point b){return (a.y-b.y)/(a.x-b.x);}
inline double angle(Point a,Point b){return a*b/len(a)/len(b);}
inline double angle(Line l){
double k=atan2(l.s.y-l.t.y,l.s.x-l.t.x);
if(sgn(k)<0) k+=pi;
if(sgn(k-pi)==0) k=pi;
return k;
}
inline double area(Point a,Point b,Point c){return (b-a)^(c-a);}//a为公共点,b到c有向面积
inline Point rotate(Point a,double rad){return Point(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));}
inline Point normal(Point a){return Point(-a.y/len(a),a.x/len(a));}//逆时针旋转90度的法向量
inline int plr(Point p,Line l){//0:on -1:left 1:right
int k=sgn((l.t-l.s)^(p-l.s));
if(k>0) return -1;
if(k<0) return 1;
return 0;
}
inline bool on_seg(Point p,Line l){return plr(p,l)==0&&sgn((p-l.s)*(p-l.t))<=0;}
inline Point proj(Point p,Line l){return l.s+(l.t-l.s)*((l.t-l.s)*(p-l.s))/len2(l.t-l.s);}
inline Point symmetry(Point p,Line l){return proj(p,l)*2-p;}
inline double dis_seg(Point p,Line l){
if(sgn((p-l.s)*(l.t-l.s))<0||sgn((p-l.t)*(l.s-l.t))<0) return min(dis(p,l.s),dis(p,l.t));
return dis(p,l);
}
inline int llr(Line l1,Line l2){//0:平行 1:相交 2:重合
if(sgn((l1.s-l1.t)^(l2.s-l2.t))==0){
if(plr(l1.s,l2)==0) return 2;
else return 0;
}
return 1;
}
inline int ssr(Line l1,Line l2){//0:不相交 1:非规范相交 2:规范相交
int c1=plr(l1.s,l2),c2=plr(l1.t,l2);
int d1=plr(l2.s,l1),d2=plr(l2.t,l1);
if(c1*c2<0&&d1*d2<0) return 2;
return (c1==0&&on_seg(l1.s,l2))||(c2==0&&on_seg(l1.t,l2))||(d1==0&&on_seg(l2.s,l1))||(d2==0&&on_seg(l2.t,l1));
}
inline int slr(Line s,Line l){//0:不相交 1:非规范相交 2:规范相交 3:重合
int d1=plr(s.s,l),d2=plr(s.t,l);
if(d1==0&&d2==0) return 3;
if(d1*d2==0) return 1;
if(d1*d2<0) return 2;
return 0;
}
inline Point cp(Line l1,Line l2){
double s1=(l1.t-l1.s)^(l2.s-l1.s);
double s2=(l1.t-l1.s)^(l2.t-l1.s);
return (l2.s*s2-l2.t*s1)/(s2-s1);
}
一般用下标从 0 开始的 Point 数组表示
cpp12345inline double area(Point *pg,int n){
double res=0;
for(int i=0;i<n;i++) res+=area(Point(0,0),pg[i],pg[(i+1)%n])/2;
return res;
}
将多边形三角剖分,计算出每个三角形的重心,然后再以三角形有向面积为权重加权平均
cpp123456inline Point polygon_center(Point *pg,int n){
Point ans(0,0);
if(area(pg,n)==0) return ans;
for(int i=0;i<n;i++) ans=ans+(pg[i]+pg[(i+1)%n])*area(Point(0,0),pg[i],pg[(i+1)%n]);
return ans/area(pg,n)/6;
}
三四象限的点极角序最小,排序后是从三四象限的点(如果有)逆时针排序
代码是以原点为基点求的极角序,如果要以其他的点为基点,则需要把所有的点都减去那个基点
cpp123456inline bool pcmp(Point a,Point b){
auto f=[](Point p){return p.y>0||(p.y==0&&p.x<0);};
if(f(a)!=f(b)) return f(a)<f(b);
else if((a^b)==0) return a.x<b.x;
else return (a^b)>0;
}
cpp12345678910111213141516171819inline int convex_hull(Point *pg,int n,Point *ch){//会将pg排序并去重,返回凸包顶点数,求的的凸包的点均为顶点,不存在两条边共线
sort(pg,pg+n,[](Point a,Point b){
if(sgn(a.x-b.x)==0) return sgn(a.y-b.y)<0;
return sgn(a.x-b.x)<0;
});
n=unique(pg,pg+n)-pg;
int cnt=0;
for(int i=0;i<n;i++){
while(cnt>=2&&sgn((ch[cnt-1]-ch[cnt-2])^(pg[i]-ch[cnt-1]))<=0) cnt--;
ch[cnt++]=pg[i];
}
int t=cnt;
for(int i=n-2;i>=0;i--){
while(cnt>=t+1&&sgn((ch[cnt-1]-ch[cnt-2])^(pg[i]-ch[cnt-1]))<=0) cnt--;
ch[cnt++]=pg[i];
}
if(n>=2) cnt--;
return cnt;
}
适用于任意多边形。具体原理是从要判断的点水平作射线,穿过了奇数条边则说明在多边形内部,反之则在多边形外部
但有几个特殊情况:如果遇到一个水平的边,则应视为没有穿过边;穿过了两条边交界处,应视为穿过了一个边;穿过了两条边构成的一个角的顶点,则应视为没有穿过边。其中前一种可以特判,后一种可以采取把线段看成上闭下开的形式来解决
cpp12345678910111213141516171819202122inline bool in_polygon(Point p,Point *pg,int n){// pg下标从0开始,在边上也算
int ans=0;
Point p1,p2;
p1=pg[0];
for(int i=1;i<=n;i++){
p2=pg[i%n];
if(on_seg(p,Line(p1,p2))) return true;
if(p1.y==p2.y){// 水平边不考虑
p1=p2;
continue;
}
if(p.y>min(p1.y,p2.y) && p.y<=max(p1.y,p2.y) && p.x<=max(p1.x,p2.x)){//上取下不取
if(p1.x==p2.x) ans++;
else{
double x=p1.x+(p.y-p1.y)/slope(p1,p2);
if(p.x<=x) ans++;
}
}
p1=p2;
}
return ans&1;
}
时间复杂度
只适用于判断点是否在凸包内
逐个计算点到凸包顶点构成的向量之间的叉乘是否是同号,即判断这些向量是否都往一个方向转,是的话就说明点在该凸包内
时间复杂度
只适用于凸包
首先把多边形划分为多个区域
如果要判断一个点是否在该凸包内,则先找到这个点在哪个区域内。如果不在这几个区域中,则必不在凸包内,否则我们只需要判断这个点和凸包的边的关系即可
时间复杂度
cpp123456789101112131415161718192021222324int n,m,k,ok=0,cnt=1;cin>>n>>m>>k;
for(int i=0;i<n;i++) cin>>p[i].x>>p[i].y;
n=convex_hull(p,n,ch);
Point base=p[0];
for(int i=0;i<n;i++) ch[i]=ch[i]-base;
while(m--){
Point now;cin>>now.x>>now.y;
now=now-base;
if(plr(now,Line(ch[0],ch[1]))==0||plr(now,Line(ch[0],ch[n-1]))==0){
if(on_seg(now,Line(ch[0],ch[1]))||on_seg(now,Line(ch[0],ch[n-1]))) ok++;
continue;
}
if(!(plr(now,Line(ch[0],ch[1]))==-1&&plr(now,Line(ch[0],ch[n-1]))==1)) continue;
int l=1,r=n-1,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if((ch[mid]^now)>=0) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==-1||ans==n-1) continue;
if(plr(now,Line(ch[ans],ch[ans+1]))!=1) ok++;
}
if(ok>=k) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
cpp1234567891011121314151617181920212223Point p[_];
int tmp[_];
double solve(int l,int r){
if(l==r) return inf;
if(l+1==r) return dis(p[l],p[r]);
int mid=(l+r)>>1,cnt=0;
double d1=solve(l,mid),d2=solve(mid+1,r),d=min(d1,d2);
for(int i=l;i<=r;i++) if(fabs(p[mid].x-p[i].x)<d) tmp[++cnt]=i;
for(int i=1;i<=cnt;i++){
for(int j=1;j<i;j++){
double d3=dis(p[tmp[i]],p[tmp[j]]);
d=min(d,d3);
}
}
return d;
}
inline void subtask(){
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y;
sort(p+1,p+n+1,[](Point a,Point b){return sgn(a.x-b.x)==0? sgn(a.y-b.y)<0:sgn(a.x-b.x)<0;});
double res=solve(1,n);
printf("%.4lf",res);
}