题解-洛谷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)\)。
\]
\]
其中 \(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 【模板】三维凸包的更多相关文章
- luogu P4724 模板 三维凸包
LINK:三维凸包 一个非常古老的知识点.估计也没啥用. 大体上了解了过程 能背下来就背下来吧. 一个bf:暴力枚举三个点 此时只需要判断所有的点都在这个面的另外一侧就可以说明这个面是三维凸包上的面了 ...
- 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)
To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...
- 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)
题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...
- 【AC自动机】洛谷三道模板题
[题目链接] https://www.luogu.org/problem/P3808 [题意] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. [题解] 不再介绍基础知识了,就是裸的模 ...
- 洛谷P3375 [模板]KMP字符串匹配
To 洛谷.3375 KMP字符串匹配 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- 题解 洛谷P5018【对称二叉树】(noip2018T4)
\(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...
- 洛谷-P5357-【模板】AC自动机(二次加强版)
题目传送门 -------------------------------------- 过年在家无聊补一下这周做的几道AC自动机的模板题 sol:AC自动机,还是要解决跳fail边产生的重复访问,但 ...
- 题解 洛谷 P3396 【哈希冲突】(根号分治)
根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...
随机推荐
- Go原生和GoFrame的Cookie关于MaxAge区别
Go原生和GoFrame的Cookie关于MaxAge区别 环境: gf v1.14.4 go 1.11 Go原生 type Cookie struct { Name string Value str ...
- [LeetCode题解]83. 删除排序链表中的重复元素 | 递归 + 迭代
方法一:递归 解题思路 通过递归法,每次判断目前头节点与给定的节点是否相等.如是,继续判断下一个节点,否则保存当前头节点,设置 next 指向下次递归得到的节点,然后返回当前节点. 代码 /** * ...
- Zookeeper集群搭建(多节点,单机伪集群,Docker集群)
Zookeeper介绍 原理简介 ZooKeeper是一个分布式的.开源的分布式应用程序协调服务.它公开了一组简单的原语,分布式应用程序可以在此基础上实现更高级别的同步.配置维护.组和命名服务.它的设 ...
- Python 调用Get接口
import requests,jsonurl = 'http://localhost:30627/api/jobs/GetNuberId?id=2'req = requests.get(url)re ...
- 我要进大厂之大数据ZooKeeper知识点(1)
01 让我们一起学大数据 老刘又回来啦!在实验室师兄师姐都找完工作之后,在结束各种科研工作之后,老刘现在也要为找工作而努力了,要开始大数据各个知识点的复习总结了.老刘会分享出自己的知识点总结,一是希望 ...
- 面试大厂,90%会被问到的Java面试题(附答案)
面向对象的三个特征 封装,继承,多态 多态的好处,代码中如何实现多态,虚拟机中如何实现多态 允许不同类对象对同一消息作出相应,好处如下: 可替换性:多态对已存在的代码具有可替换性 可扩充性:增加新的子 ...
- MGR(MySQL Group Replication)部署测试
1. 环境说明 192.168.11.131 mgr1 主节点 192.168.11.132 mgr2 从节点 192.168.11.133 mgr3 从节点 2. 在mgr1.mgr2.mgr3上安 ...
- css3系列之过渡transition
transition 设置变换属性的过渡效果,举个例子,也就是说, 如果从width100px 变成 width200px,要以什么方式进行变换,速度,时间,类型等. transition trans ...
- robot 如何定义用户关键字、变量
1.用户关键字,使用robot语法定义的关键字 2.系统关键字 自带的 3.资源文件,自己定义的关键字 4.变量 自己定义的关键字,需单独建一个资源文件 自己写的关键字,需写在***Keywords ...
- 【VUE】7.组件通信(二)子组件修改父组件
1. 前提&知识点 1./components/Father.vue 是父组件, Son.vue 是子组件 2.子组件修改父组件 emit 2. 组件通信 1. 首先对子组件绑定一个事件 ch ...