点击%XZY巨佬

向量的板子

#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\),可以推出

\[\frac{\vec b×\vec c}{\vec b×\vec a}=\frac{S_{B_1B_2EA_1}}{S_{B_1B_2DC}}=\frac{|A_1F|}{|CG|}=\frac{|A_1P|}{|\vec a|}
\]

而\(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)\)。

我们用有向直线(一个点和一个方向向量)表示半平面,以下默认半平面在有向直线的左侧。

对有向直线按方向向量的极角排序,维护一个双端队列,存储当前构成半平面的直线以及相邻两直线的交点。

每次加入一条有向直线,如果队首/队尾的交点在直线右侧(用叉积判)则弹掉队首/队尾的直线。

为什么这样是对的呢?因为加入直线的单调性,所以要被弹出的直线一定在队首或队尾。感兴趣的话可以自己手画一些例子来理解。

需要注意的细节:

  1. 加入直线时,先弹队尾,再弹队首。
  2. 最后还要检查队尾交点是否在队首直线的右侧,如果是也要弹掉。
  3. 特判平行直线,在右侧的要弹掉。
  4. 如果题目给出的半平面不一定有限制边界,则应该手动加入一个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;
}

自适应辛普森积分

求函数的定积分,应用于算不规则图形的面积。采用二次函数来拟合。

\[\int_l^r f(x)dx\approx\int_l^r(ax^2+bx+c)dx\\
=\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;
}

计算几何细节梳理&模板的更多相关文章

  1. 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)

    orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...

  2. 数论细节梳理&模板

    初阶 扩展欧拉 \(k\ge\varphi(m)\)时,\(b^k\equiv b^{k\%\varphi(m)+\varphi(m)}(\bmod m\)) 扩展CRT 推式子合并同余方程. htt ...

  3. 多项式细节梳理&模板(多项式)

    基础 很久以前的多项式总结 现在的码风又变了... FFT和NTT的板子 typedef complex<double> C; const double PI=acos(-1); void ...

  4. 各种反演细节梳理&模板

    炫酷反演魔术课件byVFK stO FDF Orz(证明全有%%%) 莫比乌斯反演 \(F(n)=\sum\limits_{d|n}f(d)\Rightarrow f(n)=\sum\limits_{ ...

  5. 【kuangbin】计算几何部分最新模板

    二维几何部分 // `计算几何模板` ; const double inf = 1e20; const double pi = acos(-1.0); ; //`Compares a double t ...

  6. 计算几何(凸包模板):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 ...

  7. 计算几何--求凸包模板--Graham算法--poj 1113

    Wall Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 28157   Accepted: 9401 Description ...

  8. C++面向对象类的书写相关细节梳理

    类的问题 继承类的原因:为了添加或者替换功能. 1. 继承时重写类的方法 v 替换功能 ① 将所有方法都设置为virtual(虚函数),以防万一. Virtual:经验表明最好将所有方法都设置为vir ...

  9. [转] POJ计算几何

    转自:http://blog.csdn.net/tyger/article/details/4480029 计算几何题的特点与做题要领:1.大部分不会很难,少部分题目思路很巧妙2.做计算几何题目,模板 ...

随机推荐

  1. Python学习第十篇——函数初步

    def make_album(name,album_name,song_nums = 1): dict_album = {name:[album_name]} if int(song_nums) &g ...

  2. Linux之hosts文件

    一.序言: 今天同事部署环境遇到问题, 原因1:修改了主机名,在/etc/hosts文件中加了3台集群的ip和主机名,但是将默认的前两行也改了,没注意看改了哪里, 现象: 1.zookeeper单台可 ...

  3. 学习 yii2.0——视图之间相互包含

    布局 首先创建一个布局文件simple.php,路径是在views/layout/目录下. <p>this is header</p> <?= $content ?> ...

  4. javax.validation.ValidationException: Unable to create a Configuration, because no Bean Validation provider could be found. Add a provider like Hibernate Validator (RI) to your classpath.

    项目依赖 <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifa ...

  5. MyBatis映射文件2(不支持自增的数据库解决方案/参数处理[单参、多参、命名参数])

    针对Oracle不支持自增的解决方案 Oracle不支持自增,但是它使用序列来模拟自增,每次插入数据的主键是从序列中拿到的值,那么如何获取这个值呢? <insert id="addEm ...

  6. 牛客练习赛13D 幸运数字4

    题目链接:https://ac.nowcoder.com/acm/contest/70/D 题目大意: 略 分析: 注意到12! < 10^9 < 13!,于是当n > 13时,第k ...

  7. vue axios 封装(二)

    封装二: http.js import axios from 'axios' import storeHelper from './localstorageHelper' // 全局设置 const ...

  8. Deploy .NET Core with Docker

    Creating a .NET Core project If you already have an existing .NET Core project you are more than wel ...

  9. c++中结构体sort()排序

    //添加函数头 #include <algorithm> //定义结构体Yoy typedef struct { double totalprice;         //总价 doubl ...

  10. BZOJ 1800 [Ahoi2009]fly 飞行棋

    题目链接 思路 终于有一道自己想出来的题了,开心. 因为是矩形,一定有直角,所以考虑直径,之后由于矩形对角线是两条直径,所以考虑组合数. 直径有n条,矩形有c(n,2)个. #include<i ...