引言
参考刘汝佳大白书,有一些自己的修改的相关代码
模板题AC 代码
UVa11178 Morley定理 点与直线计算
LA 3263 好看的一笔画 欧拉定理、点与直线计算
UVa11796 Dog Distance 点与直线计算、相对运动
LA2572 Viva Confetti 圆的计算
UVA10652 凸包
LA4728 Square 凸包、旋转卡壳
LA4973 Ardenia 三维线段距离
LA4589 Asteroids均匀密度凸多边形重心
模板
Part 0 基本部分
计算几何零散知识
精度
const double eps = 1e-9;
- 圆周率
const double PI = acos(-1);
- 比较函数
int dcmp(double x){return fabs(x)<eps?0:(x<0?-1:1);}
三角形外接圆、内切圆、旁切圆、九点圆
- PICK定理
求三角形内部格点数
int Cross(Point A,Point B){ return abs(A.x*B.y-A.y*B.x);}
int cal_on(Point A){return abs(__gcd(A.x,A.y));}
int main(){
while(a[0].input()){
a[1].input();a[2].input();
if(a[0]==Point(0,0)&&a[1]==Point(0,0)&&a[2]==Point(0,0))return 0;
int A = Cross(a[1]-a[0],a[2]-a[0]);
int b = cal_on(a[0]-a[1])+cal_on(a[0]-a[2])+cal_on(a[1]-a[2]);
printf("%d\n",(A-b+2)/2);
}
}
- 分数类
struct Rat {
ll fz, fm;
Rat(ll _fz=0):fz(_fz),fm(1){}
Rat(ll _fz,ll _fm):fz(_fz),fm(_fm){ll _=abs(__gcd(fz,fm));fz/=_;fm/=_;}
Rat operator +(const Rat &o)const{
ll x = fm/__gcd(fm,o.fm)*o.fm;
return Rat(fz*(x/fm)+o.fz*(x/o.fm),x);
}
friend Rat operator -(const Rat &now,const Rat &o){
Rat oo = o;
oo.fz=-oo.fz;
return now+oo;
}
Rat operator *(const Rat &o)const{
return Rat(fz*o.fz,fm*o.fm);
}
friend bool operator <(const Rat &now,const Rat &o){
Rat res=now-o; return res.fz<0;
}
friend bool operator >(const Rat&now,const Rat &o){
Rat res=now-o; return res.fz>0;
}
bool operator ==(const Rat &o)const{
return fz==o.fz&&fm==o.fm;
}
};
Simpson自适应积分
1.取三个点a, (a+b)/2, b
2.利用Simpson积分公式分别计算原图像在[a, b], [a, (a+b)/2], [(a+b)/2. b]的面积(分别记为S0, S1, S1),若S0与S1+S2的值相差无几,则可以认为S0为原图像在[a, b]内的面积。直接返回这个面积,否则继续划分下去
应用:求圆的面积并
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-13;
int dcmp(double x){return fabs(x)<=eps?0:(x<0?-1:1);}
struct Point{
double x,y;
Point(double _x=0,double _y=0):x(_x),y(_y){}
bool operator <(const Point& o)const{return dcmp(x-o.x)<0||(dcmp(x-o.x)==0&&dcmp(y-o.y)<0);}
bool operator == (const Point& o)const{return dcmp(x-o.x)==0&&dcmp(y-o.y)==0;}
Point operator + (const Point& o)const{return Point(x+o.x,y+o.y);}
Point operator - (const Point& o)const{return Point(x-o.x,y-o.y);}
Point operator * (double o)const{return Point(x*o,y*o);}
Point operator / (double o)const{return Point(x/o,y/o);}
int input(){return scanf("%lf%lf",&x,&y);}
};
struct Circle{
Point p;double r;
bool operator <(const Circle &o)const{
return r<o.r;
}
void input(){
p.input();scanf("%lf",&r);
}
}c[1005];
int n;
double Min=1001,Max=-1001;
bool flag[1005];
pair<double,double>a[1005];
double ans;
double length(Point a){
return sqrt(a.x*a.x+a.y*a.y);
}
double F(double x){
int cnt=0;
for(int i=1;i<=n;i++){
double dis=fabs(c[i].p.x-x);
if(dcmp(dis-c[i].r)<0){
double len=sqrt(c[i].r*c[i].r-dis*dis);
a[++cnt]=make_pair(c[i].p.y-len,c[i].p.y+len);
}
}
if(!cnt)return 0;
sort(a+1,a+cnt+1);
double l=a[1].first,r=a[1].second,ans=0;
for(int i=2;i<=cnt;i++){
if(dcmp(a[i].first-r)<=0)r=r>a[i].second?r:a[i].second;
else ans+=r-l,l=a[i].first,r=a[i].second;
}
ans+=r-l;
return ans;
}
double simpson(double l,double r,double now,double fl,double fr,double fmid){
double mid=(l+r)/2,flmid=F((l+mid)/2),frmid=F((mid+r)/2);
double p=(fl+fmid+flmid*4)*(mid-l)/6,q=(fmid+fr+frmid*4)*(r-mid)/6;
if (dcmp(now-p-q)==0) return now;
else return simpson(l,mid,p,fl,fmid,flmid)+simpson(mid,r,q,fmid,fr,frmid);
}
void solve(){
sort(c+1,c+n+1);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if (dcmp(length(c[i].p-c[j].p)+c[i].r-c[j].r)<=0){
flag[i]=true;break;
}
}
}
int tot=0;
for(int i=1;i<=n;i++){
if(!flag[i]) c[++tot]=c[i];
}
n=tot;
double fl=F(Min),fr=F(Max),fmid=F((Min+Max)/2);
ans=simpson(Min,Max,(fl+fr+fmid*4)*(Max-Min)/6,fl,fr,fmid);
printf("%.3lf\n",ans);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) {
c[i].input();
double x1 = c[i].p.x-c[i].r,x2=c[i].p.x+c[i].r;
Min = Min<x1?Min:x1;
Max = Max>x2?Max:x2;
}
solve();
}
Part 1 结构体
- 点
#define Vector Point
//二维
struct Point {
double x, y;
Point(double x = 0,double y=0):x(x),y(y){}
bool operator < (Point o) {return dcmp(x-o.x)<0 || (dcmp(x-o.x)==0&&dcmp(y-o.y)<0);}
bool operator == (Point o) {return dcmp(x-o.x) == 0 && dcmp(y-o.y) == 0;}
Vector operator + (Vector o) {return Vector (x+o.x, y+o.y);}
Vector operator - (Point o) {return Vector(x-o.x,y-o.y);}
Vector operator * (double o) {return Vector(x*o,y*o);}
Vector operator / (double o) {return Vector(x/o,y/o);}
void input() {cin>>x>>y;}
void output() {printf("(%.10f,%.10f)",x,y);}
};
//K维
struct Point {
double x[20];int k;
Point(){k=0;}
Point(int _k,...){
k=_k;va_list ap;va_start (ap,_k);
for(int i=0;i<_k;i++) x[i]=va_arg(ap,double);
va_end(ap);
}
void input() {
if(k==0) {cout<<"Error k = 0"<<endl;exit(0);}
for(int i=0;i<k;i++) scanf("%lf",&x[i]);
}
void output() {
if(k==0){cout<<"Error k = 0"<<endl;exit(0);}
printf("(%.2f",x[0]);for(int i=1;i<k;i++)printf(",%.2f",x[i]);printf(")");
}
void safe(Point o){
if(k!=o.k) {
output();o.output();
cout<<"can't compare"<<endl;exit(0);
}
}
bool operator <(Point o){
safe(o);
for(int i=0;i<k;i++){
if(dcmp(x[i]-o.x[i])!=0)return dcmp(x[i]-o.x[i]<0)?1:0;
}return 0;
}
bool operator == (Point o) {
safe(o);
for(int i=0;i<k;i++) if(dcmp(x[i]-o.x[i])!=0)return 0;
return 1;
}
Vector operator +(Vector o) {
safe(o);Vector res;res.k=k;
for(int i=0;i<k;i++)res.x[i]=x[i]+o.x[i];
return res;
}
Vector operator -(Point o) {
safe(o);Vector res;res.k=k;
for(int i=0;i<k;i++)res.x[i]=x[i]-o.x[i];
return res;
}
Vector operator *(double o) {
Vector res;res.k=k;
for(int i=0;i<k;i++)res.x[i]=x[i]*o;
return res;
}
Vector operator /(double o) {
Vector res;res.k=k;
for(int i=0;i<k;i++)res.x[i]=x[i]/o;
return res;
}
};
向量和点用同一个结构体,但意义不同
- 直线
struct Line {
Point p;Vector v;double ang;
Line(){}
Line(Point _p,Vector _v) :p(_p),v(_v){v = v/(sqrt(v.x*v.x+v.y*v.y));ang = atan2(v.y,v.x);}
bool operator < (const Line& o) const{return ang < o.ang;}
Vector Normal(Vector A) {return Vector(-A.y / Length(A),A.x / Length(A));}
void move_line(double d) { p = p + Normal(v)*d;}
void output() {p.output();putchar(' ');v.output();putchar('\n');}
};
v为方向向量,在构造函数中会将其转化成单位向量
point 方法:已知求距离p点r单位有向距离的直线上的点
重载小于:按极角排序
- 圆
struct Circle{
Point o;double r;
Circle(){};
Circle(Point o,double r):o(o),r(r){}
Point point(double a){return Point(o.x + cos(a)*r, o.y + sin(a)*r);}
void input(){o.input();cin>>r;}
};
point方法:已知角度,求圆上的点
- 平面
struct Plane{
Point p;Vector n;
Plane(Point _p,Vector _n):p(_p),n(_n){}
};
Part 2 函数
Point(Vector) & Point(Vector)
- 点积
double Dot(Vector A,Vector B){return A.x*B.x + A.y*B.y;}
- 叉积
二维
double Cross(Vector A,Vector B){return A.x*B.y - A.y*B.x;}
三维
Vector Cross(Vector A,Vector B) {
return Vector(3,
A.x[1]*B.x[2]-A.x[2]*B.x[1],
A.x[2]*B.x[0]-A.x[0]*B.x[2],
A.x[0]*B.x[1]-A.x[1]*B.x[0]);
}
- 向量长度(两点相减)
double Length(Vector A) {return sqrt(Dot(A,A));}
- 两向量转角
double Angle(Vector A,Vector B) {return acos(Dot(A,B)/Length(A)/Length(B));}
- 向量极角[-PI,PI)
double Angle(Vector v){return atan2(v.y,v.x);}
- 向量旋转
Vector Rotate(Vector A, double rad){return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));}
- 法向量
Vector Normal (Vector A){return Vector(-A.y / Length(A),A.x / Length(A));}
- 异面直线p1+su和p2+tv的公垂线对应的s。如果平行/重合,返回inf
Rat GCXJD_Line_Line(Point p1,Point u,Point p2,Point v){
Rat b = Dot(u,u)*Dot(v,v)-Dot(u,v)*Dot(u,v);
if(b.fz==0) return Rat(inf);
Rat a = Dot(u,v)*Dot(v,p1-p2)-Dot(v,v)*Dot(u,p1-p2);
return Rat(a.fz,b.fz);
}
利用叉积点积判断向量位置
括号第一个数是点积符号,第二个数是叉积符号,第一个向量水平向右,表示了第二个向量的位置- 三角形面积
二维
double Tri_Area(Point A,Point B,Point C){return Cross(B-A,C-A)/2;}
三维
double Area(Point A,Point B,Point C){
return Length(Cross(B-A,C-A))*0.5;
}
- 多边形面积(逆时针给点)
double PolygonArea(Point* p,int n){
double area = 0;
for(int i = 1; i < n-1; i++) area += Tri_Area(P[i],P[i+1],P[0]);
return area/2;
}
- 两点中点
Point Mid_Point_Point(Point A,Point B){return Point((A.x+B.x)/2,(A.y+B.y)/2);}
Point & Line
- 点到直线距离
double Point_Line_Dis(Point P,Line L){return fabs(Cross(L.v,P-L.p));}
- 点到线段距离
double Dis_Point_Seg(Point P,Point A,Point B){
if(A == B) return Length(P-A);
Vector v1 = B-A,v2 = P-A,v3 = P-B;
if(dcmp(Dot(v1,v2))<0) return Length(v2);
else if(dcmp(Dot(v1,v3))>0) return Length(v3);
else return Dis_Point_Line(P,A,B);
}
- 点到直线投影
Point TY_Point_Line(Point P,Line L){return A+L.v*Dot(L.v,P-A);}
Line & Line
- 两直线交点
Point JD_Line_Line(Line L1,Line L2){return L1.p+L1.v*(Cross(L2.v,L1.p-L2.p)/Cross(L1.v,L2.v));}
- 判断线段相交
两线段恰好有一个公共点,且不在任何一条线段的端点。
线段规范相交的充分条件是:每条线段的两个端点都在另一条线段的两侧
bool PDXJ_Seg_Seg(Point A1,Point A2,Point B1,Point B2){
double c1 = Cross(A2-A1,B1-A1),c2 = Corss(A2-A1,B2-A1),
c3 = Cross(B2-B1,A1-B1),c4 = Cross(B2-B1,A2-B1);
return dcmp(c1)*dcmp(c2) < 0 && dcmp(c3)*dcmp(c4) < 0;
}
如果允许在端点处相交,则还需要判断一个点是否在一条线段上(不包含端点)
bool PDON_Point_Seg(Point P,Point A1,Point A2){
return dcmp(Cross(A1-P,A2-P)) == 0 && dcmp(Dot(A1-P,A2-P)) < 0;
}
- 直线平移
Line Move_Line(double d){return Line(p+Normal(v)*d,v);}
Point & Circle
- 点到直线切线
int QX_Point_Circle(Point p,Circle C,Vector* v){
Vector u = C.c-p;
double dist = Length(u);
if(dist < C.r) return 0;
else if(dcmp(dist -C.r)){v[0] = Rotate(u,PI/2);return 1;}
else {
double ang = asin(C.r / dist);
v[0] = Rotate(u,-ang);v[1] = Rotate(u,ang);
return 2;
}
}
Line &Circle
- 直线与圆交点
int JD_Line_Circle(Line L,Circle C,vector<Point>& sol){
double a = L.v.x, b = L.p.x-C.o.x, c = L.v.y, d = L.p.y-C.o.y;
double e = a*a+c*c,f = 2*(a*b+c*d),g = b*b + d*d - C.r*C.r;
double delta = f*f-4*e*g,t1,t2;
if(dcmp(delta)<0) return 0;
if(dcmp(delta)==0){t1 = t2 = -f/(2*e);sol.push_back(L.point(t1));return 1;}
t1 = (-f-sqrt(delta))/(2*e);sol.push_back(L.point(t1));
t2 = (-f+sqrt(delta))/(2*e);sol.push_back(L.point(t2));
return 2;
}
Circle & Circle
- 两圆求交点
int JD_Circle_Circle(Circle C1,Circle C2,vector<Point>& sol)
{
double d=Length(C1.o-C2.o);//圆心距
if(dcmp(d)==0) return dcmp(C1.r-C2.r)==0 ? -1:0;//重合:包含
if(dcmp(C1.r + C2.r - d)<0) return 0;//相离
if(dcmp(fabs(C1.r-C2.r)-d)>0) return 0;//包含
double a = Angle(C2.o - C1.o);//圆心极角
double da = acos((C1.r*C1.r +d*d -C2.r*C2.r)/(2*C1.r*d));//余弦定理
Point p1 = C1.point(a-da),p2 = C1.point(a+da);
sol.push_back(p1);if(p1 == p2) return 1;
sol.push_back(p2);return 2;
}
- 两圆求切线
int Circle_Circle_Tangents(Circle A, Circle B, Point* a, Point* b){
int cnt=0;
if(A.r<B.r) { swap(A, B); swap(a, b); } //保证圆A比圆B大
int d2=(A.c.x-B.c.x)*(A.c.x-B.c.x)+(A.c.y-B.c.y)*(A.c.y-B.c.y);
int rdiff=A.r-B.r;
int rsum=A.r+B.r;
if(d2<rdiff*rdiff) return 0; //内含
double base=angle(B.c-A.c); // atan2(B.c.y-A.c.y,B.c.x-A.c.x);
if(d2==0&&A.r==B.r) return -1; //两圆重合,有无数条
if(d2==rdiff*rdiff){ //内切,一条切线
a[cnt]=A.point(base); b[cnt]=B.point(base); cnt++;
return 1;
}
//有外共切线
double ang=acos((A.r-B.r)/sqrt(d2));
a[cnt]=A.point(base+ang); b[cnt]=B.point(base+ang); cnt++;
a[cnt]=A.point(base-ang); b[cnt]=B.point(base-ang); cnt++;
if(d2==rsum*rsum){ //存在一条
a[cnt]=A.point(base); b[cnt]=B.point(PI+base); cnt++;
}
else if(d2>rsum*rsum){ //存在两条
double ang=acos((A.r+B.r)/sqrt(d2));
a[cnt]=A.point(base+ang); b[cnt]=B.point(PI+base+ang); cnt++;
a[cnt]=A.point(base-ang); b[cnt]=B.point(PI+base-ang); cnt++;
}
return cnt;
}
多边形
- 求凸包
vector<Point> ConvexHull(vector<Point> p) {
sort(p.begin(), p.end());
p.erase(unique(p.begin(), p.end()), p.end());
int n = p.size();
int m = 0;
vector<Point> ch(n+1);
for(int i = 0; i < n; i++) {
while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i >= 0; i--) {
while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
ch[m++] = p[i];
}
if(n > 1) m--;
ch.resize(m);
return ch;
}
- 旋转卡壳
double rotate_calipers(vector<Point>ch,int n)
{
double ans=-0x7fffffff;
ch.push_back(ch[0]);
int q=1;
for(int i=0;i<n;i++){
while(Cross(ch[q]-ch[i+1],ch[i]-ch[i+1])<Cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1]))
q = (q+1)%n;
ans=max(ans,max(length(ch[q]-ch[i]),length(ch[q+1]-ch[i+1])));
}
return ans;
}
平面
- 三点确定一个平面
- 点到平面距离
double Dis_Point_Plane(Point p,Plane p0){
return fabs(Dot(p-p0.p,p0.n));//不取绝对值得到的是有向距离
}
- 点到平面投影
Point TY_Point_Plane(Point p,Plane p0){
return p-p0.n*Dot(p-p0.p,p0.n);
}
- 线和平面求交点
直线
Point JD_Line_Plane(Point p1,Point p2,Plane p){
Vector v=p2-p1;
if(dcmp(Dot(p.n,p2-p1)==0)){
cout<<"find JD Error"<<endl;exit(0);
}
double t=(Dot(p.n,p.p-p1)/Dot(p.n,p2-p1));
return p1+v*t;
}
线段
Point JD_Seg_Plane(Point p1,Point p2,Plane p){
Vector v=p2-p1;
if(dcmp(Dot(p.n,p2-p1)==0)){
cout<<"find JD Error "<<endl;
exit(0);
}
double t=(Dot(p.n,p.p-p1)/Dot(p.n,p2-p1));
if(dcmp(t-1)>0||dcmp(t-0)<0){
cout<<"find JD Error"<<endl;exit(0);
}
return p1+v*t;
}
- 半平面交
bool on_l(Line L,Point p) {
return Cross(L.v, p-L.p)>0;
}
int BPMJ(Line* L, int n, Point* poly) {
sort(L,L+n);
int ll, rr;
Point *p = new Point[n];
Line *q = new Line[n];
q[ll=rr=0] = L[0];
for(int i = 1; i < n; i++) {
while(ll < rr && !on_l(L[i], p[rr-1])) rr--;
while(ll < rr && !on_l(L[i], p[ll])) ll++;
q[++rr] = L[i];
if(fabs(Cross(q[rr].v, q[rr-1].v)) < eps) {
rr--;
if(on_l(q[rr], L[i].p)) q[rr] = L[i];
}
if(ll < rr) p[rr-1] = JD_Line_Line(q[rr-1], q[rr]);
}
while(ll < rr && !on_l(q[ll], p[rr-1])) rr--;
if(rr - ll <= 1) return 0;
p[rr] = JD_Line_Line(q[rr],q[ll]);
int m = 0;
for(int i = ll;i <= rr;i++) poly[m++] = p[i];
return m;
}