- 计算几何
- struct of 向量
- struct of 线段
- struct of 圆
- 平面几何基本操作
- 判断两条线段是否相交
- 点是否在线段上
- 多边形面积
- 多边形的面积质心
- 二维凸包
- 旋转卡壳
- 最大空矩形 using 扫描法
- 平面最近点对 using 分治
- 最小圆覆盖 using 随机增量法
- 半面交 using S&I 算法
- struct of 整点直线
- 整点向量线性基
- 曼哈顿最小生成树
- 圆的离散化
- Delaunay 三角剖分
- struct of 三维向量
- 三维凸包
计算几何
struct of 向量
- rotate()返回逆时针旋转后的点,left()返回朝左的单位向量
- trans()返回p沿a,b拉伸的结果,arctrans()返回p在坐标系中的坐标
- 常量式写法,不要另加变量,需要加变量就再搞个struct
- 直线类在半面交里,其中包含线段交点
struct vec{
lf x,y; vec(){} vec(lf x,lf y):x(x),y(y){}
vec operator-(const vec &b){return vec(x-b.x,y-b.y);}
vec operator+(const vec &b){return vec(x+b.x,y+b.y);}
vec operator*(lf k){return vec(k*x,k*y);}
lf len(){return hypot(x,y);}
lf sqr(){return x*x+y*y;}
vec trunc(lf k=1){return *this*(k/len());}
vec rotate(double th){lf c=cos(th),s=sin(th); return vec(x*c-y*s,x*s+y*c);}
vec left(){return vec(-y,x).trunc();}
lf theta(){return atan2(y,x);}
friend lf cross(vec a,vec b){return a.x*b.y-a.y*b.x;};
friend lf cross(vec a,vec b,vec c){return cross(a-c,b-c);}
friend lf dot(vec a,vec b){return a.x*b.x+a.y*b.y;}
friend vec trans(vec p,vec a,vec b){
swap(a.y,b.x);
return vec(dot(a,p),dot(b,p));
}
friend vec arctrans(vec p,vec a,vec b){
lf t=cross(a,b);
return vec(-cross(b,p)/t,cross(a,p)/t);
}
void output(){printf("%.12f %.12f\n",x,y);}
}a[N];
vec projection(vec v,vec a,vec b){ // v 在 line(a,b) 上的投影
vec d=b-a;
return a+d*(dot(v-a,d)/d.sqr());
}
struct vec{
int x,y; vec(){} vec(int x,int y):x(x),y(y){}
vec operator-(const vec &b){return vec(x-b.x,y-b.y);}
vec operator+(const vec &b){return vec(x+b.x,y+b.y);}
void operator+=(const vec &b){x+=b.x,y+=b.y;}
void operator-=(const vec &b){x-=b.x,y-=b.y;}
vec operator*(lf k){return vec(k*x,k*y);}
bool operator==(vec b)const{return x==b.x && y==b.y;}
int sqr(){return x*x+y*y;}
void output(){printf("%lld %lld\n",x,y);}
}a[N]; const vec dn[]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
struct of 线段
struct line{
vec p1,p2; lf th;
line(){}
line(vec p1,vec p2):p1(p1),p2(p2){
th=(p2-p1).theta();
}
bool contain(vec v){ // contains the point
return cross(v,p2,p1)<=eps;
}
vec PI(line b){ // point of intersection
lf t1=cross(p1,b.p2,b.p1);
lf t2=cross(p2,b.p2,b.p1);
return vec((t1*p2.x-t2*p1.x)/(t1-t2),(t1*p2.y-t2*p1.y)/(t1-t2));
}
};
struct of 圆
struct cir{
vec v; lf r;
void PI(vec a,vec b,vec &A,vec &B){ // PI with line(a,b)
vec H=projection(v,a,b);
vec D=(a-b).trunc(sqrt(r*r-(v-H).sqr()));
A=H+D; B=H-D;
}
void PI(cir b,vec &A,vec &B){ // PI with circle
vec d=b.v-v;
lf dis=abs(b.r*b.r-r*r-d.sqr())/(2*d.len());
vec H=v+d.trunc(dis);
vec D=d.left().trunc(sqrt(r*r-dis*dis));
A=H+D; B=H-D;
}
};
平面几何基本操作
判断两条线段是否相交
- 快速排斥实验:判断线段所在矩形是否相交(用来减小常数,可省略)
- 跨立实验:任一线段的两端点在另一线段的两侧
bool judge(vec a,vec b,vec c,vec d){ // 线段ab和线段cd
#define SJ(x) max(a.x,b.x)
点是否在线段上
bool onseg(vec p,vec a,vec b){
return (a.x-p.x)*(b.x-p.x)
多边形面积
lf area(vec a[],int n){
lf ans=0;
repeat(i,0,n)
ans+=cross(a[i],a[(i+1)%n]);
return abs(ans/2);
}
多边形的面积质心
vec centre(vec a[],int n){
lf S=0; vec v=vec();
repeat(i,0,n){
vec &v1=a[i],&v2=a[(i+1)%n];
lf s=cross(v1,v2);
S+=s; v=v+(v1+v2)*s;
}
return v*(1/(3*S));
}
二维凸包
- 求上凸包,按坐标 (x, y) 字典升序排序,从小到大加入栈,如果出现凹多边形情况则出栈。下凸包反着来
- \(O(n\log n)\),排序是瓶颈
vector st;
void push(vec &v,int b){
while((int)st.size()>b
&& cross(*++st.rbegin(),st.back(),v)<=0) // 会得到逆时针的凸包
st.pop_back();
st.push_back(v);
}
void convex(vec a[],int n){
st.clear();
sort(a,a+n,[](vec a,vec b){
return make_pair(a.x,a.y)
<补充> 动态凸包
- 支持添加点、询问点是否在凸包内,\(O(\log n)\)
const lf eps=1e-7;
multiset> st; vec c;
typedef multiset>::iterator ptr;
void push(vec v){
st.insert({v.theta(),v});
}
void dec(ptr &p){if(p==st.begin())p=st.end(); --p;}
void inc(ptr &p){++p; if(p==st.end())p=st.begin();}
ptr find(vec v){
auto p=st.lower_bound({v.theta(),v}); dec(p);
return p;
}
bool out(vec v){ // whether out of the convex
v=v-c;
auto l=find(v),r=l; inc(r);
return cross(l->se,r->se,v)<-eps;
}
void init(vec v1,vec v2,vec v3){
st.clear();
c=(v1+v2+v3)*(1.0/3);
push(v1-c); push(v2-c); push(v3-c);
}
void add(vec v){ // add a point to convex
if(!out(v))return;
v=v-c;
auto l=find(v),r=l; inc(r);
auto l2=l; dec(l2);
while(cross(l->se,l2->se,v)>0){
dec(l),dec(l2);
}
auto r2=r; inc(r2);
while(cross(r->se,r2->se,v)<0){
inc(r),inc(r2);
}
inc(l);
if(l<=r)st.erase(l,r);
else st.erase(l,st.end()),st.erase(st.begin(),r);
st.insert({v.theta(),v});
}
旋转卡壳
- 每次找到凸包每条边的最远点,基于二维凸包,\(O(n\log n)\)
lf calipers(vec a[],int n){
convex(a,n); // 凸包算法
repeat(i,0,st.size())a[i]=st[i]; n=st.size();
lf ans=0; int p=1; a[n]=a[0];
repeat(i,0,n){
while(cross(a[p],a[i],a[i+1])
最大空矩形 using 扫描法
- 在范围 (0, 0) 到 (l, w) 内求面积最大的不覆盖任何点的矩形面积,\(O(n^2)\),n 是点数
- 如果是
lf
就把 vec
结构体内部、ans
、u
和 d
的类型改一下
struct vec{
int x,y; // 可能是lf
vec(int x,int y):x(x),y(y){}
};
vector a; // 存放点
int l,w;
int ans=0;
void work(int i){
int u=w,d=0;
repeat(k,i+1,a.size())
if(a[k].y>d && a[k].ya[i].y?u:d)=a[k].y; // 更新u和d
if((l-a[i].x)*(u-d)<=ans)return; // 最优性剪枝
}
ans=max(ans,(l-a[i].x)*(u-d)); // 撞墙更新ans
}
int query(){
a.push_back(vec(0,0));
a.push_back(vec(l,w)); // 加两个点方便处理
// 小矩形的左边靠着顶点的情况
sort(a.begin(),a.end(),[](vec a,vec b){return a.x
平面最近点对 using 分治
lf ans;
bool cmp_y(vec a,vec b){return a.yans)break;
upd(a[i],b[j]);
}
b[t++]=a[i];
}
}
lf nearest(int n){
ans=1e20;
sort(a,a+n,[](vec a,vec b){return a.x
最小圆覆盖 using 随机增量法
- eps可能要非常小。随机化,均摊 \(O(n)\)
struct cir{ // 圆(结构体)
vec v; lf r;
bool out(vec b){ // 点a在圆外
return (v-b).len()>r+eps;
}
cir(vec a){v=a; r=0;}
cir(vec a,vec b){v=(a+b)*0.5; r=(v-a).len();}
cir(vec a,vec b,vec c){ // 三个点的外接圆
b=b-a,c=c-a;
vec s=vec(b.sqr(),c.sqr())*0.5;
lf d=1/cross(b,c);
v=a+vec(s.x*c.y-s.y*b.y,s.y*b.x-s.x*c.x)*d;
r=(v-a).len();
}
};
cir RIA(vec a[],int n){
repeat_back(i,2,n)swap(a[rand()%i],a[i]); // random_shuffle(a,a+n);
cir c=cir(a[0]);
repeat(i,1,n)if(c.out(a[i])){
c=cir(a[i]);
repeat(j,0,i)if(c.out(a[j])){
c=cir(a[i],a[j]);
repeat(k,0,j)if(c.out(a[k]))
c=cir(a[i],a[j],a[k]);
}
}
return c;
}
半面交 using S&I 算法
vector ans; // ans: output, shows a convex hull
namespace half{
line a[N]; int n; // (a[],n): input, the final area will be the left of the lines
deque q;
void solve(){
a[n++]=line(vec(inf,inf),vec(-inf,inf));
a[n++]=line(vec(-inf,inf),vec(-inf,-inf));
a[n++]=line(vec(-inf,-inf),vec(inf,-inf));
a[n++]=line(vec(inf,-inf),vec(inf,inf));
sort(a,a+n,[](line a,line b){
if(a.th1 && !a[i].contain(r[0].PI(r[1])))q.pop_back();
while(q.size()>1 && !a[i].contain(q[0].PI(q[1])))q.pop_front();
q.push_back(a[i]);
}
while(q.size()>1 && !q[0].contain(r[0].PI(r[1])))q.pop_back();
while(q.size()>1 && !r[0].contain(q[0].PI(q[1])))q.pop_front();
#undef r
ans.clear();
repeat(i,0,(int)q.size()-1)ans<
struct of 整点直线
struct line{
ll up,down,dx,dy; // y=(dy/dx)x+(up/down) or x=(up/down)
void adjust(ll &x,ll &y){
if(x<0)x=-x,y=-y;
if(x==0)y=1;
else if(y==0)x=1;
else{
ll d=abs(__gcd(x,y));
x/=d; y/=d;
}
}
line(ll x1,ll y1,ll x2,ll y2){
dx=(x1-x2),dy=(y1-y2);
adjust(dx,dy);
if(dx!=0){
up=-dy*x1+dx*y1;
down=dx;
adjust(up,down);
}
else{
up=-dx*y1+dy*x1;
down=dy;
adjust(up,down);
}
}
pii d(){return {dx,dy};} // 斜率
pii d2(){ // 垂线斜率
ll ddx=-dy,ddy=dx;
adjust(ddx,ddy);
return {ddx,ddy};
}
bool operator==(line b)const{
return make_tuple(up,down,dx,dy)
== make_tuple(b.up,b.down,b.dx,b.dy);
}
};
struct h{ // Hash
ll operator()(line a)const{
return a.up+a.down*10000+a.dx*100000000+a.dy*1000000000000;
}
};
整点向量线性基
- n 个向量的集合 \(\{(x_i,y_i)\}\) 可以构造线性等价的两个向量的集合 \(\{(a_1,b_1),(a_2,b_2)\},(b_2=0)\),即 \(\displaystyle\{\sum_{i=1}^n t_i(x_i,y_i)\mid t\in \Z^n\}=\{t_1(a_1,b_1)+t_2(a_2,b_2)\mid t\in \Z^2\}\)
linear::push(x,y)
: 添加向量
linear::query(x,y)
: 询问向量能否被线性表示
struct linear{
ll a1,b1,a2; // (a1,b1),(a2,0)
linear(){a1=b1=a2=0;}
void push(ll x,ll y){
ll A,B,d;
exgcd(y,b1,d,A,B);
a2=__gcd(a2,abs(y*a1-x*b1)/d);
b1=d;
a1=A*x+B*a1;
if(a2)a1=(a1%a2+a2)%a2;
}
bool query(ll x,ll y){
if(b1!=0 && y%b1==0){
ll r=x-y/b1*a1;
return (a2!=0 && r%a2==0) || a2==r;
}
else if(b1==0)
return (a1!=0 && x%a1==0) || a1==x;
else return false;
}
};
曼哈顿最小生成树
- input:
n,a[i].x,a[i].y
,a[i].p
是没用的。编号从 1 开始,\(O(n\log n)\)
DSU d;
int n,w[N],c[N];
struct node{
int x,y,p;
}a[N],b[N];
vector e;
int dist(int x,int y){
return abs(a[x].x-a[y].x)+abs(a[x].y-a[y].y);
}
#define lb(x) (x&-x)
struct BIT{ // special
int t[N];
void init(){
fill(t,t+n+1,0);
}
void insert(int x,int p){
for(;x<=n;x+=lb(x))
if(w[p]<=w[t[x]])
t[x]=p;
}
int query(int x){
int ans=0;
for(;x!=0;x-=lb(x))
if(w[t[x]]<=w[ans])
ans=t[x];
return ans;
}
}bit;
void work(){
bit.init();
repeat(i,1,n+1)c[i]=b[i].y; sort(c+1,c+n+1);
sort(b+1,b+n+1,[](node a,node b){
return pii(a.x,a.y)
圆的离散化
- refer to CTSC 07 高逸涵
- 若干圆,任意两圆不相切,求未被圆覆盖的闭合图形个数
- 将圆的上下顶点和两两圆的交点的y作为事件,取相邻事件中点 \(e[i]\),分析其状态,对相邻的 \(e[i]\) 用并查集判连通
// poj 1688 但是 wa 了
DSU d;
vector ovo,e;
vector > rec[N];
vector lab[N]; int labcnt,ans;
void segunion(vector > &a){ // 区间合并至最简
if(a.empty())return;
sort(a.begin(),a.end()); int pre=0;
repeat(i,0,a.size()){
if(a[i].fi>a[pre].se-eps)a[++pre]=a[i];
else a[pre].se=max(a[pre].se,a[i].se);
}
a.erase(a.begin()+pre+1,a.end());
}
void segcomplement(vector > &a){ // 区间取反
a.push_back(pair(0,inf));
repeat_back(i,0,a.size()-1){
a[i+1].fi=a[i].se;
a[i].se=a[i].fi;
}
a[0].fi=-inf;
}
void Solve(){
int n=read(); ovo.clear(); e.clear(); labcnt=ans=0;
repeat(i,0,n){
a[i].v.x=read(),a[i].v.y=read(),a[i].r=read();
ovo.push_back(a[i].v.y-a[i].r);
ovo.push_back(a[i].v.y+a[i].r);
}
repeat(i,0,n)
repeat(j,i+1,n)
if((a[i].v-a[j].v).len()(a[i].v.x-d,a[i].v.x+d));
}
segunion(rec[j]);
segcomplement(rec[j]);
lab[j].assign(rec[j].size(),0);
repeat(i,0,lab[j].size())lab[j][i]=labcnt++;
}
d.init(labcnt);
repeat(i,0,e.size()-1){
unsigned p1=0,p2=0;
while(p1rec[i+1][p2].fi-eps && rec[i][p1].fi
Delaunay 三角剖分
const lf eps=1e-8;
struct vec{
lf x,y; int id;
explicit vec(lf a=0,lf b=0,int c=-1):x(a),y(b),id(c){}
bool operator<(const vec &a)const{
return x::iterator c;
edge(int id=0){this->id=id;}
};
int cmp(lf v){return abs(v)>eps?(v>0?1:-1):0;}
lf cross(const vec &o,const vec &a,const vec &b){
return(a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
lf dot(const vec3D &a,const vec3D &b){return a.x*b.x+a.y*b.y+a.z*b.z;}
vec3D cross(const vec3D &a,const vec3D &b){
return vec3D(a.y*b.z-a.z*b.y,-a.x*b.z+a.z*b.x,a.x*b.y-a.y*b.x);
}
vector ans; // 三角剖分结果
struct DT{ // 使用方法:直接solve()
list a[N]; vec v[N]; int n;
void solve(int _n,vec _v[]){
n=_n;
copy(_v,_v+n,v);
sort(v,v+n);
divide(0,n-1);
ans.clear();
for(int i=0;i0 &&
cmp(cross(c,a,d))*cmp(cross(c,d,b))>0;
}
void addedge(int u,int v){
a[u].push_front(edge(v));
a[v].push_front(edge(u));
a[u].begin()->c=a[v].begin();
a[v].begin()->c=a[u].begin();
}
void divide(int l,int r){
if(r-l<=2){
for(int i=l;i<=r;i++)
for(int j=i+1;j<=r;j++)addedge(i,j);
return;
}
int mid=(l+r)/2;
divide(l,mid); divide(mid+1,r);
int nowl=l,nowr=r;
for(int update=1;update;){
update=0;
vec vl=v[nowl],vr=v[nowr];
for(auto i:a[nowl]){
vec t=v[i.id];
lf v=cross(vr,vl,t);
if(cmp(v)>0 || (cmp(v)== 0 && vr.dist2(t)0 && (ch==-1 || incircle(vl,vr,v[ch],v[i.id])<0)){
ch=i.id,side=-1;
}
for(auto i:a[nowr])
if(cmp(cross(vr,v[i.id],vl))>0 && (ch==-1 || incircle(vl,vr,v[ch],v[i.id])<0)){
ch=i.id,side=1;
}
if(ch==-1)break;
if(side==-1){
for(auto it=a[nowl].begin();it!=a[nowl].end();){
if(intersection(vl,v[it->id],vr,v[ch])){
a[it->id].erase(it->c);
a[nowl].erase(it++);
}
else it++;
}
nowl=ch;
addedge(nowl,nowr);
}
else{
for(auto it=a[nowr].begin();it!=a[nowr].end();){
if(intersection(vr,v[it->id],vl,v[ch])){
a[it->id].erase(it->c);
a[nowr].erase(it++);
}
else it++;
}
nowr=ch;
addedge(nowl,nowr);
}
}
}
}dt;
vec a[N]; DSU d;
vector e[N]; // 最小生成树结果
void mst(){ // 求最小生成树
dt.solve(n,a);
sort(ans.begin(),ans.end(),[](const pii &A,const pii &B){
return a[A.fi].dist2(a[A.se])
struct of 三维向量
trunc(K)
返回 K
在 *this
上的投影向量
rotate(P,L,th)
返回点 P
绕轴 (O,L)
旋转 th
弧度后的点
rotate(P,L0,L1,th)
返回点 P
绕轴 (L0,L1)
旋转 th
弧度后的点
struct vec{
lf x,y,z; vec(){} vec(lf x,lf y,lf z):x(x),y(y),z(z){}
vec operator-(vec b){return vec(x-b.x,y-b.y,z-b.z);}
vec operator+(vec b){return vec(x+b.x,y+b.y,z+b.z);}
vec operator*(lf k){return vec(k*x,k*y,k*z);}
bool operator<(vec b)const{return make_tuple(x,y,z)
三维凸包
- 将所有凸包上的面放入面集
f
中,其中 face::p[i]
作为 a
的下标,\(O(n^2)\)
const lf eps=1e-9;
struct vec{
lf x,y,z;
vec(lf x=0,lf y=0,lf z=0):x(x),y(y),z(z){};
vec operator-(vec b){return vec(x-b.x,y-b.y,z-b.z);}
lf len(){return sqrt(x*x+y*y+z*z);}
void shake(){ // 微小扰动
x+=(rand()*1.0/RAND_MAX-0.5)*eps;
y+=(rand()*1.0/RAND_MAX-0.5)*eps;
z+=(rand()*1.0/RAND_MAX-0.5)*eps;
}
}a[N];
vec cross(vec a,vec b){
return vec(
a.y*b.z-a.z*b.y,
a.z*b.x-a.x*b.z,
a.x*b.y-a.y*b.x);
}
lf dot(vec a,vec b){return a.x*b.x+a.y*b.y+a.z*b.z;}
struct face{
int p[3];
vec normal(){ // 法向量
return cross(a[p[1]]-a[p[0]],a[p[2]]-a[p[0]]);
}
lf area(){return normal().len()/2.0;}
};
vector f;
bool see(face f,vec v){
return dot(v-a[f.p[0]],f.normal())>0;
}
void convex(vec a[],int n){
static vector c;
static bool vis[N][N];
repeat(i,0,n)a[i].shake(); // 防止四点共面
f.clear();
f.push_back((face){0,1,2});
f.push_back((face){0,2,1});
repeat(i,3,n){
c.clear();
repeat(j,0,f.size()){
bool t=see(f[j],a[i]);
if(!t) // 加入背面
c.push_back(f[j]);
repeat(k,0,3){
int x=f[j].p[k],y=f[j].p[(k+1)%3];
vis[x][y]=t;
}
}
repeat(j,0,f.size())
repeat(k,0,3){
int x=f[j].p[k],y=f[j].p[(k+1)%3];
if(vis[x][y] && !vis[y][x]) // 加入新面
c.push_back((face){x,y,i});
}
f.swap(c);
}
}