平面几何一直都算是我(可能是每一个人)比较头疼的一个问题,因为用cpp做几何题的一个最大的问题是精度,如果搞不好这个东西,那么很有可能会功亏一篑,所以如何用合理的语言构造一个相对完善的数学体系就变成了这个任务的重中之重,之前那位大佬的题解里面包含了一个功能极其强大,并且比较严谨的集合模板,经过了很长时间的翻译,解释和验算(还真的发现几处大佬的疏漏),这个模板基本被我搞定了,有了这个模板,基本上所有的平面几何问题都可迎刃而解了,不过我还是建议自行验证一下(可能还有问题也说不定),而且掌握一些数学知识还是必要的,这样在遇到这些问题的变体的时候我们也能很快地利用现有的强大的工具找出解决的办法,这些工具无非就是简化一些计算量而已。
注释写的已经听清楚的了
2018 7 31
之前又做了一个平面几何,结果硬是没算对,所以这次又结合了一些其他大佬的模板,增加了一些功能,这样那道题就顺利解决了。(果然还是要积累更多,另外鉴于计算集合的严密性,我认为这个是非常适合使用一些模板的(自己推有时候会吐血,记一些结论非常重要))
模板更新
# include <cstdio>
# include <cstring>
# include <cmath>
# include <vector>
# include <algorithm>
using namespace std;
# define mp make_pair
//挂
namespace io {
const int L=(1<<21)+1;
char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
inline void flush() {
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
}
inline void putc(char x) { *oS++=x; if (oS==oT) flush(); }
template<class I> inline void gi(I&x) {
for (f=1,c=gc();c<'0'||c>'9';c=gc()) if (c=='-') f=-1;
for (x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15); x*=f;
}
template<class I> inline void print(I x) {
if (!x) putc('0');
if (x<0) putc('-'),x=-x;
while (x) st[++tp]=x%10+'0',x/=10;
while (tp) putc(st[tp--]);
}
inline void gs(char*s, int&l) {
for (c=gc();c<'a'||c>'z';c=gc());
for (l=0;c<='z'&&c>='a';c=gc()) s[l++]=c;
s[l]=0;
}
inline void ps(const char*s) { for (int i=0,n=strlen(s);i<n;i++) putc(s[i]); }
struct IOFLUSHER{ ~IOFLUSHER() { flush(); } } _ioflusher_;
};
using io::putc;
using io::gi;
using io::gs;
using io::ps;
using io::print;
//
/*
说明:所有角度为弧度制(对应math函数)
*/
//定义+极小量
typedef double db;
#define dbformat "%lf"
const db eps=1e-8;
//判断正负0 (有点精妙)
inline int dcmp(const db &x) { return (x>eps)-(x<-eps); }
inline db sqr(db x){ return x * x; }
//构造二维坐标点变量
struct P{
db x,y;
inline db mo2() { return x*x+y*y; }//模平方
inline db mo() { return sqrt(mo2()); }//模
inline db angle() { return atan2(y,x); }//稳定地(相对于atan)计算方位角(范围-pai到pai)
inline void readint() { gi(x),gi(y); }//整数输入(不一定是int)
inline void readdb() { scanf(dbformat dbformat,&x,&y); }//分数输入
inline P rot90() { return (P){-y,x}; }//逆时针旋转90度
inline P rot(db r) {//旋转矩阵法计算逆时针旋转r度坐标
db Sin=sin(r),Cos=cos(r);
return (P){x*Cos-y*Sin,x*Sin+y*Cos};
}
inline void uni() {//向量单位化
db d=mo();
if (dcmp(d))
x/=d,y/=d;
}
};
//定义各种运算和比较
inline bool operator < (const P &a,const P &b) { return dcmp(a.x-b.x)?a.x<b.x:a.y<b.y; }//先x后y
inline bool operator == (const P &a,const P&b) { return !dcmp(a.x-b.x)&&!dcmp(a.y-b.y); }//x与y相差不到eps
inline bool operator != (const P &a,const P &b) { return dcmp(a.x-b.x)||dcmp(a.y-b.y); }//前一个取反
inline P operator + (const P &a,const P &b) { return (P){a.x+b.x,a.y+b.y}; }//向量相加
inline P operator - (const P &a,const P &b) { return (P){a.x-b.x,a.y-b.y}; }//向量相减
inline P operator * (const P &p,const db &t) { return (P){p.x*t,p.y*t}; }//数乘
inline P operator / (const P &p,const db &t) { return (P){p.x/t,p.y/t}; }//数除
inline db operator * (const P &a,const P &b) { return a.x*b.y-a.y*b.x; }//叉积(a * b = a.mo() * b.mo() * sin(b.angle() - a.angle()))
inline db dot(const P &a,const P&b) { return a.x*b.x+a.y*b.y; }//点积(dot(a , b) = a.mo() * b.mo() * cos(b.angle() - a.angle()))
//点积的夹角可以认为是小于180度的cos正值,然而叉积的必须严格从a向b(因为可能有正负问题)
//垂直时,点积为0,平行或共线时,叉积为0
inline db dis_point2(const P &a,const P &b) { return (a-b).mo2(); }//距离平方
inline db dis_point(const P &a,const P &b) { return (a-b).mo(); }//距离
//向量变量(两点决定,有方向问题(计算角时))固定向量非自由向量
struct line{
P a,b;
inline db angle() { return (b-a).angle(); }//计算角度
};
//这里开始复杂了一点,作者应该是记录了注释,然而因为现实问题全成了乱码 ,我保留了tm,作为重要的标志(就连作者都留下了注释)
//判断是否在向量所在直线
inline bool in_point_line(const P &p,const line &l) {//?е????????
return dcmp((p-l.a)*(l.b-l.a))==0;
}
//判断点是否处于向量所在直线或者向量所指方向的左侧
inline bool in_halfplane(const P &p,const line &l) {//?е?????????
return (p-l.a)*(l.b-l.a)<=eps;
}
//判断是否处于向量内部(包含首尾端点)
inline bool in_point_segment(const P &p,const line &l) {//?е????????
return in_point_line(p,l)&&dcmp(dot(p-l.a,l.b-l.a))>=0&&dcmp(dot(p-l.b,l.a-l.b))>=0;
}
//点到直线垂直距离
inline db dis_point_line(const P &p,const line &l) {//????????
return fabs((p-l.a)*(l.b-l.a))/dis_point(l.a,l.b);
}
//判断是否相交
inline bool cross_segment_segment(const line &a,const line &b) {//?ж???????????
return dcmp((b.a-a.a)*(a.b-a.a))*dcmp((b.b-a.a)*(a.b-a.a))!=1&&
dcmp((a.a-b.a)*(b.b-b.a))*dcmp((a.b-b.a)*(b.b-b.a))!=1;
}
//计算交点
inline P cross_line_line(const line &a,const line &b) {//??????
db s1=(b.a-a.a)*(a.b-a.a);
db s2=(a.b-a.a)*(b.b-a.a);
return b.a+(b.b-b.a)*(s1/(s1+s2));
}
//计算点到一条固定向量上点的最小距离
inline db dis_point_segment(const P &p,const line &l) {//????ξ???
if (dot(p-l.a,l.b-l.a)<=0) return dis_point(p,l.a);
if (dot(p-l.b,l.a-l.b)<=0) return dis_point(p,l.b);
return dis_point_line(p,l);
}
//计算两固定向量上点最短距离
inline db dis_segment_segment(const line &a,const line &b) {//???????ξ???
if (cross_segment_segment(a,b))
return 0;
return min(min(dis_point_segment(a.a,b),dis_point_segment(a.b,b))
,min(dis_point_segment(b.a,a),dis_point_segment(b.b,a)));
}
//点到直线垂线与直线的交点
inline P proj_point_line(const P &p,const line &l) {//????????????
return l.a+(l.b-l.a)*(dot(p-l.a,l.b-l.a)/dis_point2(l.b,l.a));
}
//定义圆变量
struct circle{ P o; db r; };
//点与圆,正在圆外,0上,负内
inline int in_point_cirlce(const P &p,const circle &c) {//???????λ?? 1:?? 0:??? -1:???
return dcmp(dis_point(p,c.o)-c.r);
}
//线与圆,正与圆相离,0相切,负相交
inline int in_line_circle(const line &l,const circle &c) {//????????λ?? 1:???? 0:???? -1:??
return dcmp(dis_point_line(c.o,l)-c.r);
}
//圆与圆关系(a半径更大b半径更小,如果不满足会交换)???(原模板出错了,修改了一下)
//两个判别值(ar-br , ar + br)
// 0<ar - br 1==ar - br 2<ar+br 3==ar + br 4>ar + br
// 内含 内切 相交 外切 相离
inline int in_circle_circle(const circle &a,const circle &b) {//?????λ?ù????
//0:??? 1:???? 2:?? 3:???? 4:????
if (a.r<b.r) return in_circle_circle(b,a);
db dis=dis_point(a.o,b.o);
if (dcmp(dis-(a.r+b.r))>=0) return dcmp(dis-(a.r+b.r))+3;
return dcmp(dis+b.r-a.r)+1;
}
//求直线与圆两个交点
inline pair<P,P> cross_line_circle(const line &l,const circle &c) {//????????????
P o=proj_point_line(c.o,l);
P d=(l.b-l.a);d.uni();
db len=sqrt(c.r*c.r-dis_point2(c.o,o));
d=d*len;
return mp(o-d,o+d);
}
//求圆与圆的交点(用到余弦定理)
inline pair<P,P> cross_circle_circle(const circle &a,const circle &b) {//??????????
db len=dis_point2(a.o,b.o);
db t=a.r*a.r+len-b.r*b.r;
P e=a.o+(b.o-a.o)*(t/2/len);
db p=sqrt(a.r*a.r-dis_point2(a.o,e));//???又错了(已修改)
P d=(b.o-a.o).rot90()*(p/sqrt(len));
return mp(e+d,e-d);
}
//求点与圆切点 (用到了射影定理)
inline pair<P,P> proj_point_circle(P p,const circle &c) {//???????е?
db dis2=dis_point2(p,c.o);
db h=c.r*c.r*(dis2-c.r*c.r)/dis2;
db w=sqrt(c.r*c.r-h);h=sqrt(h);
p=p-c.o;p.uni();
return mp(c.o+p*w+p.rot90()*h,c.o+p*w-p.rot90()*h);
}
//后头两个感觉没什么用,懒得检查了
//相离(或外切内切相交)两圆外公切线 (类平行)
inline pair<line,line> proj_circle_circle_out(const circle &a,const circle &b) {//???????????????
if (!dcmp(a.r-b.r)) {
P p=(b.o-a.o).rot90();p.uni();
return mp((line){a.o+p*a.r,b.o+p*b.r},(line){a.o-p*a.r,b.o-p*b.r});
}
if (a.r<b.r)
return proj_circle_circle_out(b,a);
pair<P,P>p=proj_point_circle(b.o,(circle){a.o,a.r-b.r});
P d1=(p.first-a.o)*(b.r/(a.r-b.r));
P d2=(p.second-a.o)*(b.r/(a.r-b.r));
return mp((line){p.first+d1,b.o+d1},(line){p.first+d2,b.o+d2});
}
//相离(或外切)两圆内公切线(交叉)
inline pair<line,line> proj_circle_circle_in(const circle &a,const circle &b) {//????????????????
if (a.r<b.r)
return proj_circle_circle_in(b,a);
pair<P,P>p=proj_point_circle(b.o,(circle){a.o,a.r+b.r});
P d1=(p.first-a.o)*(b.r/(a.r+b.r));
P d2=(p.first-a.o)*(b.r/(a.r+b.r));
return mp((line){p.first-d1,b.o-d1},(line){p.second-d2,b.o-d2});
}
//自行脑补部分
const long double pai = acos(-1);
//三角形
struct tri
{
P a, b, c;
};
//三角形有向面积(带正负)向量ca叉cb
inline db tri_s(tri x)
{
return 0.5 * ((x.a - x.c) * (x.b - x.c));
}
//重载类型
inline db tri_s(P a , P b , P c)
{
return 0.5 * ((a - c) * (b - c));
}
//三角形面积
inline db tri_as(tri x)
{
return 0.5 * fabs((x.a - x.c) * (x.b - x.c));
}
//重载类型
inline db tri_as(P a , P b , P c)
{
return 0.5 * fabs((a - c) * (b - c));
}
//两向量夹角(有向a到b , 范围-pai到pai)
inline db ang(P a , P b)
{
db xita = pai + b.angle() - a.angle();
if(dcmp(xita - pai) > 0)
xita -= 2 * pai;
return xita;
}
//绝对值的那个角
inline db a_ang(P a , P b)
{
return fabs(ang(P a , P b));
}
//三角形顶点为C时与圆相交时的面积
inline db Triangle_cross_Circle(P A , P B , P C, db r){
db a, b, c, x, y;
db s = tri_s(A , B , C);
a = (B-C).mo();
b = (A-C).mo();
c = (A-B).mo();
if (a <= r && b <= r) return s;
else if (a<r && b>=r) {
x = (dot(A-B,C-B) + sqrt(c*c*r*r - sqr((A-B)*(C-B))))/c;
return asin(s*(c-x)*2.0/c/b/r)*r*r*0.5 + s*x/c;
}
else if (a >= r && b<r) {
y = (dot(B-A,C-A) + sqrt(c*c*r*r - sqr((B-A) * (C-A))))/c;
return asin(s*(c-y)*2.0/c/a/r)*r*r*0.5 + s*y/c;
}
else {
if (dcmp(fabs(2.0*s) - r*c) >= 0 || dot(B-A,C-A) <= 0 || dot(A-B,C-B) <= 0) {
if (dot(A-C,B-C) < 0) {
if ((A-C) * (B-C) < 0) return (-pai - asin(s*2.0/a/b))*r*r*0.5;
else return (pai - asin(s*2.0/a/b))*r*r*0.5;
}
else return asin(s*2.0/a/b)*r*r*0.5;
}
else {
x = (dot(A-B,C-B) + sqrt(c*c*r*r - sqr((A-B) * (C-B))))/c;
y = (dot(B-A,C-A) + sqrt(c*c*r*r - sqr((B-A) * (C-A))))/c;
return (asin(s*(1-x/c)*2/r/b) + asin(s*(1-y/c)*2/r/a))*r*r*0.5 + s*((y+x)/c-1);
}
}
}
//计算多边形面积
inline db duobian_s(vector<P> &X)
{
int i, siz = X.size();
db ans = 0;
for(i = 0 ; i < siz ; i++)
ans += X[i] * X[(i + 1) % siz];
return 0.5 * fabs(ans);
}
//终极杀器,多边形与圆重合面积
inline db duobian_circle_s(vector<P> &X , circle c)
{
int i, siz = X.size();
db ans = 0;
for (i = 0 ; i < siz ; i++)
res += Triangle_cross_Circle(X[i] , X[(i + 1) % siz] , c.o , c.r);
return fabs(ans);
}
int main()
{
return 0;
}