模板(from 紫书):
#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld eps=1e-10;
const ld PI=acos((ld)(-1.0));
struct Point
{
ld x,y;
Point(ld x=0,ld y=0) : x(x),y(y) {}
};
typedef Point Vector;
Vector operator + (Vector a, Vector b) { return Vector(a.x+b.x, a.y+b.y); }
Vector operator - (Vector a, Vector b) { return Vector(a.x-b.x, a.y-b.y); }
Vector operator * (Vector a, ld p) { return Vector(a.x*p, a.y*p); }
Vector operator / (Vector a, ld p) { return Vector(a.x/p, a.y/p); }
bool operator < (const Point& a, const Point &b)
{
return a.x<b.x||(a.x==b.x&&a.y<b.y) ;
}
int dcmp(ld x)
{
if(fabs(x)<eps) return 0; else return x<0 ? -1 : 1;
}
bool operator == (const Point &a, const Point &b)
{
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
// Vector a; 的极角是 atan2(a.y,a.x);
ld operator * (Vector a, Vector b) { return a.x*b.x+a.y*b.y; }
ld operator ^ (Vector a, Vector b) { return a.x*b.y-b.x*a.y; }
ld length(Vector a) { return sqrt(a*a); }
ld angle(Vector a, Vector b) { return acos(a*b/length(a)/length(b)); }
ld area3(Point a, Point b, Point c) { return (b-a)^(c-a); }
Vector rotate(Vector a, ld rad) { return Vector(a.x*cos(rad)-a.y*sin(rad), a.x*sin(rad)+a.y*cos(rad)); }
Vector Normal(Vector a) { ld L = length(a); return Vector(-a.y/L,a.x/L); }
//直线(P+tv)和(Q+tw)的交点
//调用前用求两条直线有唯一交点,当且仅当v^w!=0 。
Point getLineInserction(Point P, Vector v, Point Q, Vector w)
{
Vector u = P-Q;
ld t=(w^u)/(v^w);
return P+v*t;
}
//求P到直线AB的距离,A和B是直线上的任意两点。
ld distanceToLine(Point P, Point A, Point B)
{
Vector v1 = B-A, v2 = P-A;
return fabs(v1^v2) / length(v1) ;
}
//求P到线段AB的距离,A和B是线段的端点。
ld distanceToSegment(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(v1*v2)<0) return length(v2);
else if(dcmp(v1*v3)>0) return length(v3);
else return fabs(v1^v2) / length(v1);
}
//求出P在直线AB上的投影
Point getLineProjection(Point P, Point A, Point B)
{
Vector v=B-A;
return A+v*((v*(P-A))/(v*v));
}
// 判断线段a1a2与线段b1b2是否相交
bool isSegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
{
ld c1 = (a2-a1)^(b1-a1), c2=(a2-a1)^(b2-a1),
c3 = (b2-b1)^(a1-b1), c4=(b2-b1)^(a2-b1);
return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
//判断点是否在一条线段上
bool onSegment(Point p, Point a1, Point a2)
{
return dcmp((a1-p)^(a2-p))==0&&dcmp((a1-p)*(a2-p)) < 0;
}
int main()
{
return 0;
}
例题:
UVA 11178 Morley定理:
用到了直线旋转,直线交点和直线夹角
Point A,B,C;
Point solve(Point A, Point B, Point C)
{
ld radABC=angle(A-B,C-B),radACB=angle(A-C,B-C);
Vector BD=rotate(C-B,radABC/3);
Vector CD=rotate(A-C,radACB*2/3);
return getLineInserction(B,BD,C,CD);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int xa,ya,xb,yb,xc,yc;
scanf("%d%d%d%d%d%d",&xa,&ya,&xb,&yb,&xc,&yc);
A=Point(xa,ya);
B=Point(xb,yb);
C=Point(xc,yc);
Point D=solve(A,B,C);
Point E=solve(B,C,A);
Point F=solve(C,A,B);
printf("%.15f %.15f %.15f %.15f %.15f %.15f\n",(double)D.x,(double)D.y,(double)E.x,(double)E.y,(double)F.x,(double)F.y);
}
return 0;
}
LA 3263 :一笔画。
这里有一个常用的公式叫做欧拉定理 :
在平面图(在图论中,平面图是可以画在平面上并且使得不同的边可以互不交叠的图。而如果一个图无论怎样都无法画在平面上,并使得不同的边互不交叠,那么这样的图不是平面图,或者称为非平面图。)上,
V+F−E=2
,其中
V
是顶点数,
所以这题求出顶点数和面数就行了。顶点数分为原有的点和相交生成的点,相交生成的点可以在沿着笔画的时候判断是否与之前的边相交就行了。这部分复杂度是
边数可以枚举每条边,然后枚举每个点,如果点在边上,那么边数+1。复杂度为
O(n3)
。
const int N=307;
Point p[N],v[N*N];
int main()
{
int n,kase=1;
while(~scanf("%d",&n))
{
if(n==0) break;
for(int i=0;i<n;++i)
{
int x,y;
scanf("%d%d",&x,&y);
v[i]=p[i]=Point(x,y);
}
--n;
int c=n,e=n;
for(int i=0;i<n;++i)
for(int j=i-2;j>=0;--j)
if(isSegmentProperIntersection(p[i],p[i+1],p[j],p[j+1]))
v[c++]=getLineIntersection(p[i],p[i+1]-p[i],p[j],p[j+1]-p[j]);
sort(v,v+c);
c=unique(v,v+c)-v;
for(int i=0;i<n;++i)
for(int j=0;j<c;++j)
if(onSegment(v[j],p[i],p[i+1])) ++e;
printf("Case %d: There are %d pieces.\n",kase++,2+e-c);
}
return 0;
}
UVA 11796 :狗的距离。
题意是给定两条折线,两条狗分别从起点开始匀速跑,到终点的时间一样。要求两条狗的最大距离减去最小距离的差值。
假如狗在两条线段上跑,假设一条狗静止不动,另一条狗的相对运动形成一条线段,很容易算出点到线段的距离:
假设A和B是A狗和B狗走的线段。可以看出两条红线是起始AB相对位置向量和终点AB相对位置向量。后面那条红线可以用子线描述,所以B的相对运动路径为Sb->Sb+B-A。因此就能算了。
然后扩展到这题。将每次到拐点前的线段画出来,然后用上述过程算就行了。上图颜色相同的部分是要一起计算的段。复杂度是
O(2n)
。
int T,n,m;
Point pa[57],pb[57];
ld MAX,MIN;
Point read_point()
{
int x,y;
scanf("%d%d",&x,&y);
return Point(x,y);
}
void update(Point P, Point A, Point B)
{
// cout << P.x << " " << P.y << " " << A.x << " " << A.y << " " << B.x << " " << B.y <<endl;
MIN=min(MIN,distanceToSegment(P,A,B));
MAX=max(MAX,length(P-A));
MAX=max(MAX,length(P-B));
}
int main()
{
scanf("%d",&T);
int kase=1;
while(T--)
{
MAX=0,MIN=INF;
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i) pa[i]=read_point();
for(int i=0;i<m;++i) pb[i]=read_point();
ld LA=0,LB=0;
for(int i=1;i<n;++i) LA+=length(pa[i]-pa[i-1]);
for(int i=1;i<m;++i) LB+=length(pb[i]-pb[i-1]);
int sa=0,sb=0;
Point PA=pa[0],PB=pb[0];
while(sa<n-1&&sb<m-1)
{
ld la=length(pa[sa+1]-PA);
ld lb=length(pb[sb+1]-PB);
ld T=min(la/LA,lb/LB);
Vector va=(pa[sa+1]-PA)/la*T*LA;
Vector vb=(pb[sb+1]-PB)/lb*T*LB;
update(PA,PB,PB+vb-va);
PA=PA+va;
PB=PB+vb;
if(PA==pa[sa+1]) ++sa;
if(PB==pb[sb+1]) ++sb;
}
printf("Case %d: %d\n",kase++,(int)lround(MAX-MIN));
}
return 0;
}