计算几何 学习笔记

当然这个东西不常考,学一点基本的就行

叉积

\(\vec a\times \vec b\),或者简写成 axb。

\[a*b=x_1y_2-x_2y_1 \]

并不满足交换律。

这个可以这样理解,将 \(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++\)

posted @ 2022-04-01 14:54  BlankAo  阅读(39)  评论(0编辑  收藏  举报