计算几何细节梳理&模板
向量的板子
#include<bits/stdc++.h>
#define I inline
using namespace std;
typedef double DB;
struct Vec{
DB x,y;
I Vec(){x=y=0;}
I Vec(DB a){x=a;y=0;}
I Vec(DB a,DB b){x=a;y=b;}
I friend istream&operator>>(istream&cin,Vec&a){return cin>>a.x>>a.y;}
I friend ostream&operator<<(ostream&cou,Vec a){return cou<<a.x<<' '<<a.y;}
I Vec operator-(){return Vec(-x,-y);}
I Vec operator+(Vec a){return Vec(x+a.x,y+a.y);}
I Vec operator-(Vec a){return Vec(x-a.x,y-a.y);}
I Vec operator*(DB a){return Vec(x*a,y*a);}
I Vec operator/(DB a){return Vec(x/a,y/a);}
I friend Vec operator*(DB a,Vec b){b.x*=a,b.y*=a;return b;}
I friend Vec operator/(DB a,Vec b){b.x/=a,b.y/=a;return b;}
I Vec&operator+=(Vec a){x+=a.x,y+=a.y;return*this;}
I Vec&operator-=(Vec a){x-=a.x,y-=a.y;return*this;}
I Vec&operator*=(DB a){x*=a,y*=a;return*this;}
I Vec&operator/=(DB a){x/=a,y/=a;return*this;}
I DB operator*(Vec a){return x*a.x+y*a.y;}
I DB operator^(Vec a){return x*a.y-y*a.x;}
I friend bool operator<(Vec a,Vec b){
return (a^b)>0||((a^b)==0&&Len(a)<Len(b));
}
I friend DB Len(Vec a){
return sqrt(a.x*a.x+a.y*a.y);
}
I friend Vec Turn(Vec a,DB r){
const DB c=cos(r),s=sin(r);
return Vec(a.x*c-a.y*s,a.y*c+a.x*s);
}
};
初阶
向量运算
点积:\(x_1x_2+y_1y_2\),是一个向量在另一个向量上的投影
叉积:\(x_1y_2-x_2y_1\),是两个向量形成的平行四边形的有向面积
用途很广,搬一张图
旋转公式
\((x,y)\)转\(r\)弧度\(\rightarrow(x\cos r-y\sin r,y\cos r+x\sin r)\)
点到直线距离
算一个叉积,除以底边长就得到了高,也就是点到直线距离。
DB Dis(Vec&a,Vec&b1,Vec&b2){
return fabs((b1-a)^(b2-a))/Len(b1-b2);
}
直线交点&线段交点
点斜式要各种特判,不是理想的实现方法
考虑这样的转化方式:叉积->面积比->高的比->长度比->坐标
如下图,两直线相交于\(P\),\(F\)和\(G\)是垂足,有\(\triangle A_1PF\sim\triangle CB_1G\),可以推出
\]
而\(P=A_1+\frac{|A_1P|}{|\vec a|}\vec{a}\),然后就做出来了。
蒟蒻到现在好像还是背不了代码,只能背下面这张图了TAT
如何判断线段相交呢?先把直线交点求出来,再点积判一下位置关系不就好啦
Vec LineCross(Vec a1,Vec a2,Vec b1,Vec b2){
a2-=a1;b2-=b1;
return b2^a2?a1+(b2^(b1-a1))/(b2^a2)*a2:Vec(NAN,NAN);
}
Vec SegCross(Vec&a1,Vec&a2,Vec&b1,Vec&b2){
Vec c=LineCross(a1,a2,b1,b2);
return (a1-c)*(a2-c)>0||(b1-c)*(b2-c)>0?Vec(NAN,NAN):c;
}
判断点是否在多边形内
过点任作一条射线(当然平行坐标轴的最方便啦),与多边形交奇数次则在多边形内。
凸包
极角排序
可以用atan2
,也可以直接用叉积判断。
把最下面的点的坐标找出来,把所有点整体平移,这时候所有点都不在\(x\)轴下面了,在\(180°\)内叉积可以起到比较方向的作用。
代码见凸包部分。
求凸包
单调栈维护。如图,\((P_i-P_{t-1})×(P_t-P_{t-1})\ge0\),则\(P_t\)不在凸包上,需要弹掉。
洛谷P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows
I bool Polar(Vec&a,Vec&b){return (a^b)>0||((a^b)==0&&Len(a)<Len(b));}
//即Vec模板里重载的小于号。共线向量把短的放前面,求凸包的时候方便弹掉
I int Convex(Vec*a,Vec*e){
R n=e-a,k=0;
for(R i=1;i<n;++i)
if(a[i].y<a[k].y||(a[i].y==a[k].y&&a[i].x<a[k].x))k=i;
swap(a[0],a[k]);
const Vec tmp=a[0];
for(R i=0;i<n;++i)a[i]-=tmp;
sort(a+1,a+n,Polar);
R*st=new int[n],p=0;
for(R i=1;i<n;st[++p]=i++)
while(p&&((a[i]-a[st[p-1]])^(a[st[p]]-a[st[p-1]]))>=0)--p;
for(R i=0;i<=p;++i)a[i]=a[st[i]]+tmp;
return p+1;
}
int main(){
R n;cin>>n;
for(R i=1;i<=n;++i)cin>>a[i];
n=Convex(a+1,a+n+1);
DB ans=Len(a[1]-a[n]);
for(R i=1;i<n;++i)ans+=Len(a[i+1]-a[i]);
printf("%.2lf\n",ans);
return 0;
}
判断点是否在凸包内
需要令凸包最下面的点为\((0,0)\),不要还原。
把点的坐标也做相应的变换之后,丢进数组里lower_bound
找到与它极角最接近的两个点形成的线段,叉积判即可。
bool Inside(Vec*a,Vec*e,Vec v){
R i=lower_bound(a,e,v)-a-1;
return ((a[i+1]-a[i])^(v-a[i]))>=0;
}
多边形面积
对于凸多边形的理解:将多边形划分成若干个三角形,每个三角形的面积是两个向量叉积的\(\frac12\)。
update:其实任意多边形面积都可以用三角剖分法求,详见洛谷日报 计算几何初步 by wjyyy。
DB PolygonArea(Vec*a,Vec*e){
R n=e-a;DB s=0;
for(R i=2;i<n;++i)s+=(a[i-1]-a[0])^(a[i]-a[0]);
return s/2;
}
进阶算法
凸包
旋转卡壳
利用凸包的单调性,在枚举点的时候更新决策点(对踵点)并更新答案。
洛谷P1452 Beauty Contest
int main(){
R n;cin>>n;
for(R i=1;i<=n;++i)cin>>a[i];
n=Convex(a+1,a+n+1);
DB ans=0;
a[++n]=a[1];//最后加一个点
for(R i=1,p=1;i<n;++i){
while(Dis(a[p],a[i],a[i+1])<Dis(a[p+1],a[i],a[i+1]))p=p%n+1;
ans=max(ans,max(Len2(a[p]-a[i]),Len2(a[p]-a[i+1])));
}
printf("%.0lf\n",ans);
return 0;
}
半平面交
XZY巨佬提到了一种\(O(n^2)\)的算法。其实,如果不是在线插入的话,可以做到\(O(n\log n)\)。
我们用有向直线(一个点和一个方向向量)表示半平面,以下默认半平面在有向直线的左侧。
对有向直线按方向向量的极角排序,维护一个双端队列,存储当前构成半平面的直线以及相邻两直线的交点。
每次加入一条有向直线,如果队首/队尾的交点在直线右侧(用叉积判)则弹掉队首/队尾的直线。
为什么这样是对的呢?因为加入直线的单调性,所以要被弹出的直线一定在队首或队尾。感兴趣的话可以自己手画一些例子来理解。
需要注意的细节:
- 加入直线时,先弹队尾,再弹队首。
- 最后还要检查队尾交点是否在队首直线的右侧,如果是也要弹掉。
- 特判平行直线,在右侧的要弹掉。
- 如果题目给出的半平面不一定有限制边界,则应该手动加入一个INF边界。
另有洛谷P3222 [HNOI2012]射箭 的题解
模板题:洛谷P4196 [CQOI2006]凸多边形
struct Line{
Vec p,v;DB ang;
I Line(){}
I Line(Vec a,Vec b){p=a,v=b-a,ang=atan2(v.y,v.x);}
I bool operator<(Line&a){return ang<a.ang;}
I bool Right(Vec&a){return (v^(a-p))<-EPS;}
I friend Vec Cross(Line&a,Line&b){return a.p+(b.v^(b.p-a.p))/(b.v^a.v)*a.v;}
}a[N],q[N];
DB HalfPlane(Line*a,Line*e){
R n=e-a,h=0,t=0;
sort(a,e);q[0]=a[0];
for(R i=1;i<n;++i){
while(h<t&&a[i].Right(k[t-1]))--t;
while(h<t&&a[i].Right(k[h]))++h;
if(q[t].ang!=a[i].ang)q[++t]=a[i];
else if(a[i].Right(q[t].p))q[t]=a[i];
if(h<t)k[t-1]=Cross(q[t-1],q[t]);
}
while(h<t&&q[h].Right(k[t-1]))--t;
k[t]=Cross(q[t],q[h]);
return PolygonArea(k+h,k+t+1);
}
int main(){
R n,m,t=0;
cin>>n;
for(R i=1;i<=n;++i){
Vec fst,lst,now;
cin>>m>>fst;lst=fst;
for(R j=2;j<=m;++j)
cin>>now,a[++t]=Line(lst,now),lst=now;
a[++t]=Line(lst,fst);
}
printf("%.3lf\n",HalfPlane(a+1,a+t+1));
return 0;
}
闵可夫斯基和
对于欧氏空间的两个点集\(A,B\),其闵可夫斯基和为点集\(C=\{a+b|a\in A,b\in B\}\)
其中加法就是向量加法。
接下来我们只讨论凸包的闵可夫斯基和。
一个四边形,一个三角形,两个凸包的闵可夫斯基和会长什么样子呢?
因为凸包的特性,我们只需要取其中一个凸集所有最外层的点,将另一个凸多边形沿这个点向量移动,就可以得到闵可夫斯基和。
观察一下它的特点:外面正好有\(7\)个点,\(7\)条边,有没有注意到\(7=4+3\)呢?
实际上,对于任意两个凸包来说,它们的闵可夫斯基和与它们的每一条边按极角序顺次相连所得的图形都是全等的,也是一个凸包。
那岂不是很好求?二路归并即可。需要处理三点共线的情况,了撇一点的话直接扔进Convex
函数里再求一遍凸包就好了。
洛谷P4557 [JSOI2018]战争
typedef long long DB;//不涉及小数运算,直接开longlong
int Convex(Vec*a,Vec*e,Vec&bs){
R n=e-a,k=0,p=0;
for(R i=1;i<n;++i)
if(a[i].y<a[k].y||(a[i].y==a[k].y&&a[i].x<a[k].x))k=i;
swap(a[0],a[k]);bs+=a[0];
for(R i=n-1;~i;--i)a[i]-=a[0];
sort(a,e);
for(R i=1;i<n;st[++p]=i++)
while(p&&((a[i]-a[st[p-1]])^(a[st[p]]-a[st[p-1]]))>=0)--p;
for(R i=0;i<=p;++i)a[i]=a[st[i]];//做出来没还原
return a[p+1]=0,p+1;
}
void Minkowski(R n,R m){
for(R i=n;i;--i)a[i]-=a[i-1];
for(R j=m;j;--j)b[j]-=b[j-1];
for(R i=1,j=1,k=0;i<=n||j<=m;++k)
c[k]=c[k-1]+(i<=n&&(j>m||a[i]<b[j])?a[i++]:b[j++]);
}
int main(){
ios::sync_with_stdio(0);
R n=in(),m=in(),q=in();
Vec bs=0,v;
for(R i=0;i<n;++i)cin>>a[i];
for(R i=0;i<m;++i)cin>>b[i],b[i]=-b[i];
n=Convex(a,a+n,bs);
m=Convex(b,b+m,bs);
Minkowski(n,m);
n=Convex(c,c+n+m+1,v);
while(q--)
cin>>v,cout<<Inside(c,c+n,v-bs)<<'\n';
return 0;
}
其它
最小圆覆盖
把点集random_shuffle
第一层枚举一个点,如果不在当前圆中,令其为圆心,半径设为\(0\)。
第二层枚举另一个点,如果不在当前圆中,将圆更新为以当前两个点的连线为直径的圆。
第三层枚举另一个点,如果不在当前圆中,将圆更新为当前三个点的外接圆。
期望复杂度\(O(n)\)。
洛谷P1742 最小圆覆盖
typedef long double DB;//此题略卡精度
I void PerpLine(Vec a1,Vec a2,Vec&b1,Vec&b2){//中垂线
b1=(a1+a2)/2;b2=b1+Turn(a1-a2);
}
I void CircleCoverage(Vec*a,Vec*e,Vec&O,DB&r){
R n=e-a;
for(R i=0;i<n;++i){
if(Len(a[i]-O)<=r)continue;
O=a[i],r=0;
for(R j=0;j<i;++j){
if(Len(a[j]-O)<=r)continue;
O=(a[i]+a[j])/2,r=Len(a[i]-a[j])/2;
Vec a2=O+Turn(a[i]-a[j]),b1,b2;
for(R k=0;k<j;++k){
if(Len(a[k]-O)<=r)continue;
PerpLine(a[i],a[k],b1,b2);
O=LineCross(O,a2,b1,b2),r=Len(a[i]-O);
}
}
}
}
int main(){
R n;cin>>n;
for(R i=1;i<=n;++i)cin>>a[i];
srand(20020307);
random_shuffle(a+1,a+n+1);
Vec O;DB r=0;
CircleCoverage(a+1,a+n+1,O,r);
cout<<fixed<<setprecision(10)<<r<<endl<<O<<endl;
return 0;
}
自适应辛普森积分
求函数的定积分,应用于算不规则图形的面积。采用二次函数来拟合。
=\frac a3(r-l)^3+\frac b2(r-l)^2+c(r-l)\\
=\frac{r-l}6[2a(r^2-rl+l^2)+3b(r-l)+6c]\\
=\frac{r-l}6[f(l)+f(r)+4f(\frac{l+r}2)]\]
不断分割区间并迭代,直到误差控制在EPS以内。
因为误差和区间长度有关,所以可以将EPS也进行迭代,每次除以\(2\)。
洛谷P4526 【模板】自适应辛普森法2
#include<bits/stdc++.h>
#define DB double
using namespace std;
DB a,EPS=1e-6;
inline DB f(DB x){
return pow(x,a/x-x);
}
inline DB Calc(DB l,DB r){
return (r-l)*(f(l)+f(r)+4*f((l+r)/2))/6;
}
DB Simpson(DB l,DB r,DB ans,DB EPS){
DB m=(l+r)/2,al=Calc(l,m),ar=Calc(m,r);
return fabs(al+ar-ans)<EPS?ans:Simpson(l,m,al,EPS/2)+Simpson(m,r,ar,EPS/2);
}
int main(){
cin>>a;
if(a<0)puts("orz");
else printf("%.5lf\n",Simpson(EPS,20,Calc(0,20),EPS));
return 0;
}
计算几何细节梳理&模板的更多相关文章
- 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)
orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...
- 数论细节梳理&模板
初阶 扩展欧拉 \(k\ge\varphi(m)\)时,\(b^k\equiv b^{k\%\varphi(m)+\varphi(m)}(\bmod m\)) 扩展CRT 推式子合并同余方程. htt ...
- 多项式细节梳理&模板(多项式)
基础 很久以前的多项式总结 现在的码风又变了... FFT和NTT的板子 typedef complex<double> C; const double PI=acos(-1); void ...
- 各种反演细节梳理&模板
炫酷反演魔术课件byVFK stO FDF Orz(证明全有%%%) 莫比乌斯反演 \(F(n)=\sum\limits_{d|n}f(d)\Rightarrow f(n)=\sum\limits_{ ...
- 【kuangbin】计算几何部分最新模板
二维几何部分 // `计算几何模板` ; const double inf = 1e20; const double pi = acos(-1.0); ; //`Compares a double t ...
- 计算几何(凸包模板):HDU 1392 Surround the Trees
There are a lot of trees in an area. A peasant wants to buy a rope to surround all these trees. So a ...
- 计算几何--求凸包模板--Graham算法--poj 1113
Wall Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 28157 Accepted: 9401 Description ...
- C++面向对象类的书写相关细节梳理
类的问题 继承类的原因:为了添加或者替换功能. 1. 继承时重写类的方法 v 替换功能 ① 将所有方法都设置为virtual(虚函数),以防万一. Virtual:经验表明最好将所有方法都设置为vir ...
- [转] POJ计算几何
转自:http://blog.csdn.net/tyger/article/details/4480029 计算几何题的特点与做题要领:1.大部分不会很难,少部分题目思路很巧妙2.做计算几何题目,模板 ...
随机推荐
- p141一致有界原则
1.Mk取sup 为什么只要取∩就好了 不应该是先并后交吗 2.为什么说是闭集?
- win64位安装python-mysqldb1.2.3
在其他版本的mysqldb里面时间查询有问题最后确定还是在 1.2.5 版本下来解决,需要解决的问题就是这个:“Cannot open include file: 'config-win.h': No ...
- Django之admin中管理models中的表格
Django之admin中管理models中的表格 django中使用admin管理models中的表格时,如何将表格注册到admin中呢? 具体操作就是在项目文件夹中的app文件夹中的admin中注 ...
- Python_架构、同一台电脑上两个py文件通信、两台电脑如何通信、几十台电脑如何通信、更多电脑之间的通信、库、端口号
1.架构 C/S架构(鼻祖) C:client 客户端 S:server 服务器 早期使用的一种架构,目前的各种app使用的就是这种架构,它的表现形式就是拥有专门的app. B/S架构(隶属于C/ ...
- 阿里云ECS服务器云监控(cloudmonitor)Go语言版本插件安装卸载与维护
云监控Go语言版本插件安装_主机监控_用户指南_云监控-阿里云https://help.aliyun.com/document_detail/97929.html 云监控cloudmonitor 1. ...
- 优化CSS重排重绘与浏览器性能
关于CSS重排和重绘的概念,最近看到不少这方面的文章,觉得挺有用,在制作中考虑浏览器的性能,减少重排能够节省浏览器对其子元素及父类元素的重新渲染:避免过分的重绘也能节省浏览器性能:优化动画,使用3D启 ...
- Linux基础学习笔记5-软件管理
包管理器 二进制应用程序的组成部分: 二进制文件.库文件.配置文件.帮助文件 程序包管理器: debian:deb文件.dpkg包管理器 redhat:rpm文件.rpm包管理器 rpm:Redhat ...
- re正则表达式-1
匹配/查找/替换/分割函数: import re re.match('aa','aabbcc') 返回对象中span为开始位置和结束位置 re.match('aa','bbaacc') #返回值为No ...
- web service 异常
1.org/apache/commons/discovery/tools/DiscoverSingleton Exception in thread "main" java.lan ...
- python学习笔记(1)--python特点
python诞生于复杂的信息系统时代,是计算机时代演进的一种选择. python的特点,通用语言,脚本语言,跨平台语言.这门语言可以用于普适的计算,不局限于某一类应用,通用性是它的最大特点.pytho ...