计算几何 学习笔记
当然这个东西不常考,学一点基本的就行
叉积
\(\vec a\times \vec b\),或者简写成 axb。
并不满足交换律。
这个可以这样理解,将 \(a\) 逆时针旋转到 \(b\) 的角度。所以如果 \(>0\) 那么 \(a\) 就在 \(b\) 的下方,否则就在上方。
判断一个点在直线的哪边
设这个直线是 \(PQ\),而且我要求 \(A\) 在直线的哪边。计算出直线的方向向量 \(v=PQ\),那么判断 \(v*PA\) 的正负性即可(\(>0\) 在下方,\(<0\) 在上方)。
求点集的凸包
用 Andrew 算法求出 \(n\) 个点的凸包。
首先将它们以 \(x\) 为第一关键字,\(y\) 为第二关键字排序。那么最左边和最右边的点就一定会在凸包中。我们依次求出上凸包和下凸包。
用一个栈维护现在的凸包点集,如果我们现在要加入一个新点 \(a\),我们就看加入它后可以弹出栈顶多少个点。用叉积去计算斜率,比较 \(s_{top},s_{top-1}\) 的斜率,和 \(a,s_{top-1}\) 的斜率即可,若凹下去了就弹栈。
while(top>1 && cross(vet(sak[top-1],sak[top]),vet(sak[top-1],i))<0 )top--;
求一个点是否在一个三角形内
对于三角形 \(\bigtriangledown ABC\),和点 \(N\),我们看,是否 \(A,N\) 在直线 \(BC\) 同侧; \(B,N\) 在直线 \(AC\) 同侧;\(C,N\) 在直线 \(AB\) 同侧。三个都满足就是,否则就不在三角形内。
求多边形的周长与面积
周长:直接相邻两个点算距离,最后再加起来。
面积:\(\Large S=\frac{1}2 |\sum\limits_{i=1}^np_i\times p_{i+1}|\)(如果 \(i=n\) 则 \(i+1=1\))
平衡树维护凸包
CF70D。(当然下面的代码不是这一题的 233)
#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define per(i,x,y) for(int i=x;i>=y;--i)
#define lon long long
#define lod long double
#define iter set <drop> :: iterator
using namespace std;
const int n7=201234,inf=2e9;
const lod eps=1e-12;
struct drop{
lod x,y;
friend bool operator == (drop p,drop q){return p.x==q.x&&p.y==q.y;}
friend bool operator != (drop p,drop q){return !(p==q);}
friend bool operator < (drop p,drop q){return p.x==q.x?p.y<q.y:p.x<q.x;}
friend drop operator - (drop p,drop q){return (drop){p.x-q.x,p.y-q.y};}
friend lod operator * (drop p,drop q){return p.x*q.y-p.y*q.x;}
}a[n7],infl,infr;
struct iron{char typ;int t,l,r;}qt[n7];
int n,T,vis[n7],ans[n7],xmax,ymax;
set <drop> sut;
int rd(){
int shu=0;bool fu=0;char ch=getchar();
while( !isdigit(ch) ){if(ch=='-')fu=1;ch=getchar();}
while( isdigit(ch) )shu=(shu<<1)+(shu<<3)+ch-'0',ch=getchar();
return fu?-shu:shu;
}
lod getK(drop p,drop q){
if(p.x==q.x)return inf;
return (p.y-q.y)/(p.x-q.x);
}
void print(iter it=sut.end()){
puts("------");
if(it==sut.end()){
it=sut.begin();
while(it!=sut.end())printf("%.0Lf %.0Lf\n",(*it).x,(*it).y),++it;
}
else printf("%.0Lf %.0Lf\n",(*it).x,(*it).y);
}
bool check(drop x,iter it){
if( *prev(it)==infl || *it==infr )return 1;
drop p=*prev(it),q=*it;
drop z1=p-q,z2=x-p;
return z1*z2+eps<0;
}
void work(drop x){
if( sut.count(x) )return;
iter it=sut.lower_bound(x);
if( !check(x,it) )return;
while( *it!=infr && *next(it)!=infr ){
if( getK(*it,x)<=getK(*next(it),x) )++it,sut.erase( prev(it) );
else break;
}
it=--sut.lower_bound(x);
while( *it!=infl && *prev(it)!=infl ){
if( getK(*it,x)>=getK(*prev(it),x) )--it,sut.erase( next(it) );
else break;
}
sut.insert(x);
}
int main(){
n=rd();
rep(i,1,n)a[i]=(drop){rd(),rd()};
T=rd();
rep(i,1,T){
char sys=getchar();
while( !isalpha(sys) )sys=getchar();
if(sys=='c')qt[i]=(iron){'c',rd(),rd(),rd()};
if(sys=='q')qt[i]=(iron){'q',rd()},vis[ qt[i].t ]=1;
}
infl=(drop){-inf,-inf},infr=(drop){inf,-inf};
sut.insert(infl),sut.insert(infr);
rep(i,1,n){
if(vis[i])continue;
work(a[i]);
if(a[i].y>ymax)xmax=a[i].x,ymax=a[i].y;
}
per(i,T,1){
if(qt[i].typ=='c'){
drop z=(drop){qt[i].l*1.0/qt[i].t,qt[i].r*1.0/qt[i].t};
if(z.x<=xmax&&z.y<=ymax)ans[i]=1;
else if( sut.count(z) )ans[i]=1;
else{
iter it=sut.lower_bound(z);
ans[i]=!check(z,it);
}
}
if(qt[i].typ=='q'){
work(a[ qt[i].t ]);
if(a[ qt[i].t ].y>ymax)xmax=a[ qt[i].t ].x,ymax=a[ qt[i].t ].y;
}
}
rep(i,1,T)if(qt[i].typ=='c')puts(ans[i]?"no":"yes");
return 0;
}
旋转卡壳
双指针维护两个端点即可。
详细一点:设定两个指针 \(i,j\),如果 \(dis(i,j)<dis(i,j+1)\) 那么 \(j++\),否则 \(i++\)。