洛谷P4724 【模板】三维凸包

给出空间中 \(n\) 个点 \(p_i\),求凸包表面积。

数据范围:\(1\le n\le 2000\)。


这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积的讲解。


三位向量的运算

  • 模长: 即向量长度,\(|\vec{a}|=\sqrt{x_a^2+y_a^2+z_a^2}\)。

  • 点积: 标量 \(\vec{a}\cdot\vec{b}=|\vec{a}||\vec{b}|\cos<\vec{a},\vec{b}>=x_ax_b+y_ay_b+z_az_b\),为 \(\vec{a}\) 的模长乘以 \(\vec{b}\) 在 \(\vec{a}\) 上的投影的模长。

  • 叉积: 向量 \(\vec{a}*\vec{b}=(y_az_b-z_ay_b,z_ax_b-x_az_b,x_ay_b-y_ax_b)\),模长为平四面积。

上图 \(\vec{AC}*\vec{AB}=\vec{AD}\),\(\vec{AD}\) 垂直 \(\vec{AC}\) 与 \(\vec{AB}\) 的平面,模长为平四面积。


会用到的计算与判定

  • 判断点 \(E\) 在平面 \(ABC\) 上方:

作 \(\vec{AD}=\vec{AC}*\vec{AB}\),用 \(\vec{AE}\cdot \vec{AD}>0\) 来判断 \(\angle DAE<\frac{\pi}{2}\)。

  • 求点 \(E\) 到平面 \(ABC\) 的距离:

距离 \({\rm dist}(E,\triangle ABC)=EG=AF=\frac{\vec{AD}\cdot \vec{AE}}{|\vec{AD}|}=\frac{\vec{AD}\cdot \vec{AE}}{|\vec{AC}*\vec{AB}|}\)。


处理凸包

设凸包为 \(Con\),用逆时针顺序三个点表示一个三角形面。

每加入一个新点 \(p_{new}\) 的时候,把它当作光源照向之前的凸包,将未照到的面留下,加上 \(p_{new}\) 和光影边缘形成的新面。

引用巨佬的图:

判断照不照得到用判定“点 \(E\) 在平面 \(ABC\) 上方”的方法。

判断光影边缘用 \(vis\) 数组。\(vis_{i,j}\) 表示 \((i,j,k)\)(即 \((i,j)\) 逆时针方向上的面)这个面是否照光,如果 \([vis_{i,j}=1]\&\&[vis_{j,i}=0]\),说明 \((i,j)\) 是光影边缘,需加面 \((i,j,p_{new})\)。

重复加点,得到 \(m\) 个 \(Con\) 上的面 \(f_i=(A,C,B)\)。

\[\sum_{i=1}^m S_i=\sum\frac{|\vec{AC}*\vec{AB}|}{2}
\]
\[V_{Con}=\sum \frac{\frac{|\vec{AC}*\vec{AB}|}{2}\cdot {\rm dist}(D,f_i)}{3}
\]

其中 \(D\) 是一个定点,需要在 \(Con\) 内或表面上,可以选 \(p_1\),上面是三棱锥体积计算公式。


时间复杂度 \(\Theta(n^2)\),空间复杂度 \(\Theta(n^2)\)。

每加入一个点,面最多增加 \(2\) 个。

证明:设光影边缘上有 \(n\) 个点,因为每个面是三角形,所以要去掉的面 \(\ge n-2\)(中间可能有点),增加的面数为 \(n\),所以增加的点数 \(\le 2\)。


代码

  • 求表面积
#include <bits/stdc++.h>
using namespace std; //Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define be(a) a.begin()
#define en(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f; //Data
const int N=2000;
const db eps=1e-9;
int n,m;
db ans; //Convex
mt19937 orz(time(0));
db reps(){return (1.*(orz()%98)/97-.5)*eps;}
struct point{
db x,y,z;
void shake(){x+=reps(),y+=reps(),z+=reps();}
db len(){return sqrt(x*x+y*y+z*z);}
point operator-(point p){return (point){x-p.x,y-p.y,z-p.z};}
point operator*(point p){return (point){y*p.z-p.y*z,z*p.x-p.z*x,x*p.y-p.x*y};}
db operator^(point p){return x*p.x+y*p.y+z*p.z;}
}a[N];
struct plane{
int v[3];
point flag(){return (a[v[1]]-a[v[0]])*(a[v[2]]-a[v[0]]);}
db area(){return flag().len()/2;}
int see(point p){return ((p-a[v[0]])^flag())>0;}
}f[N],g[N];
int vis[N][N];
void Convex(){
#define ft f[j].v[t]
#define bk f[j].v[(t+1)%3]
f[m++]=(plane){0,1,2},f[m++]=(plane){2,1,0};
for(int i=3;i<n;i++){
int cnt=0,b;
for(int j=0;j<m;j++){
if(!(b=f[j].see(a[i]))) g[cnt++]=f[j];
for(int t=0;t<3;t++) vis[ft][bk]=b;
}
for(int j=0;j<m;j++)
for(int t=0;t<3;t++)
if(vis[ft][bk]&&!vis[bk][ft]) g[cnt++]=(plane){ft,bk,i};
m=cnt;
for(int j=0;j<m;j++) f[j]=g[j];
}
} //Main
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y>>a[i].z,a[i].shake();
Convex();
for(int i=0;i<m;i++) ans+=f[i].area();
cout.precision(3);
cout<<fixed<<ans<<'\n';
return 0;
}
  • 求体积
#include <bits/stdc++.h>
using namespace std; //Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define be(a) a.begin()
#define en(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f; //Data
const int N=2000;
const db eps=1e-9;
int n,m;
db ans; //Convex
mt19937 orz(time(0));
db reps(){return (1.*(orz()%98)/97-.5)*eps;}
struct point{
db x,y,z;
void shake(){x+=reps(),y+=reps(),z+=reps();}
db len(){return sqrt(x*x+y*y+z*z);}
point operator-(point p){return (point){x-p.x,y-p.y,z-p.z};}
point operator*(point p){return (point){y*p.z-p.y*z,z*p.x-p.z*x,x*p.y-p.x*y};}
db operator^(point p){return x*p.x+y*p.y+z*p.z;}
}a[N];
struct plane{
int v[3];
point flag(){return (a[v[1]]-a[v[0]])*(a[v[2]]-a[v[0]]);}
db area(){return flag().len()/2;}
db dist(point p){return fabs(((p-a[v[0]])^flag())/flag().len());}
int see(point p){return ((p-a[v[0]])^flag())>0;}
}f[N],g[N];
int vis[N][N];
void Convex(){
#define ft f[j].v[t]
#define bk f[j].v[(t+1)%3]
f[m++]=(plane){0,1,2},f[m++]=(plane){2,1,0};
for(int i=3;i<n;i++){
int cnt=0,b;
for(int j=0;j<m;j++){
if(!(b=f[j].see(a[i]))) g[cnt++]=f[j];
for(int t=0;t<3;t++) vis[ft][bk]=b;
}
for(int j=0;j<m;j++)
for(int t=0;t<3;t++)
if(vis[ft][bk]&&!vis[bk][ft]) g[cnt++]=(plane){ft,bk,i};
m=cnt;
for(int j=0;j<m;j++) f[j]=g[j];
}
} //Main
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y>>a[i].z,a[i].shake();
Convex();
for(int i=0;i<m;i++) ans+=f[i].area()*f[i].dist(a[0])/3;
cout.precision(2);
cout<<fixed<<ans<<'\n';
return 0;
}

祝大家学习愉快!

题解-洛谷P4724 【模板】三维凸包的更多相关文章

  1. luogu P4724 模板 三维凸包

    LINK:三维凸包 一个非常古老的知识点.估计也没啥用. 大体上了解了过程 能背下来就背下来吧. 一个bf:暴力枚举三个点 此时只需要判断所有的点都在这个面的另外一侧就可以说明这个面是三维凸包上的面了 ...

  2. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  3. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  4. 【AC自动机】洛谷三道模板题

    [题目链接] https://www.luogu.org/problem/P3808 [题意] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. [题解] 不再介绍基础知识了,就是裸的模 ...

  5. 洛谷P3375 [模板]KMP字符串匹配

    To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...

  6. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  7. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  8. 洛谷-P5357-【模板】AC自动机(二次加强版)

    题目传送门 -------------------------------------- 过年在家无聊补一下这周做的几道AC自动机的模板题 sol:AC自动机,还是要解决跳fail边产生的重复访问,但 ...

  9. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

随机推荐

  1. binary hacks读数笔记(readelf基本命令)

    一.首先对readelf常用的参数进行简单说明: readelf命令是Linux下的分析ELF文件的命令,这个命令在分析ELF文件格式时非常有用,下面以ELF格式可执行文件test为例详细介绍: 1. ...

  2. binary hacks读数笔记(ld 链接讲解 二)

    这块将介绍一下ld链接命令的具体使用.ld的作用:ld是GNU binutils工具集中的一个,是众多Linkers(链接器)的一种.完成的功能自然也就是链接器的基本功能:把各种目标文件和库文件链接起 ...

  3. Mysql的下载,安装,远程连接,密码加密规则修改。

    第一次接触mysql,,很多地方不懂,出了很多问题.本来应该在Linux系统中安装mysql的,但是奈何各种电脑限制,所以在公司电脑的Windows service R2 系统上装了mysql数据库. ...

  4. Matlab项目经验分享-去除震荡点

    Matlab是做科研是比较常用的建模工具,我在研一做项目期间遇到了一个还算比较基础的问题,所以我打算记录下来并分享出来! 处理问题步骤: 1. 抛出问题 2. 思考解决方法 3. 代码验证看结果 抛出 ...

  5. [原题复现+审计][网鼎杯 2018] WEB Fakebook(SSRF、反序列化、SQL注入)

    简介  原题复现:  考察知识点:SSRF.反序列化.SQL注入  线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题 过 ...

  6. Nmap详解

    扫描方式 -Pn/-P0:扫描前不用ping测试目标是否可达,默认所有目标端口都可达 -sT:TCP Connect扫描,进行完整的TCP三次握手,该类型扫描已被检测,且会在目标日志中记录大量连接请求 ...

  7. Linux学习 - 02 使用 - Centos8 - 网络配置相关

    『Centos8 网络配置』 题外话:最近太忙,利用仅有的周末空闲时间记录点东西,草率了. 问题1:安装 Centos8.2 minimal 过程中,只是设置了 WiFi的静态IP,没有进行[以太网] ...

  8. 看阿里P7讲MyBatis:从MyBatis的理解以及配置和实现全帮你搞懂

    前言 MyBatis 是一款优秀的持久层框架,一个半 ORM(对象关系映射)框架,它支持定制化 SQL.存储过程以及高级映`射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结 ...

  9. 在线思维导图Ayoa可以用来梳理双十一优惠规则哦

    一年一度的双十一又要来了,小伙伴们是否准备好开始买买买了呢?今年双十一,遇上英雄联盟S10总决赛,1/4决赛苏宁对上京东也让这个"电商大战"产生了很多有趣的梗.当然在玩梗的同时,广 ...

  10. FL Studio12如何进行图示编辑

    FL Studio在国内被大家 亲切的称为"水果"深受喜爱玩电音的音乐人的追捧,本章节采用图文结合的方式给大家讲解它的FL Studio12是如何进行图示编辑的. 单击图示按钮可以 ...