题解-洛谷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 【哈希冲突】(根号分治)
根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...
随机推荐
- kafka生产者数据可靠性保证
为保证 producer 发送的数据,能可靠的发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack(ackn ...
- 测试:OGG初始化同步表,源端抽取进程scn<源端事务的start_scn时,这个变化是否会同步到目标库中?
一.测试目标 疑问,OGG初始化同步表,源端抽取进程开始抽取的scn<源端事务的start_scn时,这个变化是否会同步到目标库中? 二.实验测试 如下进行测试! session 1 SQL&g ...
- linux中ugo权限管理(chmod/chown)
查看ugo权限: ll [root@localhost test]# ll total 12 -rwxr-xr-x 2 root root 4 Oct 3 11:44 a lrwxrwxrwx 1 ...
- linux文本模式和文本替换功能
linux文本有:正常模式,编辑模式,可视化模式,命令模式. 正常模式进入编辑模式下的快捷键: i --光标当前位置输入 a --光标位置后输入(append) I --行首输入 A --行尾输入 ...
- linux下内存释放
细心的朋友会注意到,当你在linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都没有看到有什么很好解决的办法 ...
- Nginx实例
一.反向代理 反向代理实例一 1.实现效果 打开浏览器,在浏览器地址栏输入地址www.pluto.com,跳转到 liunx 系统 tomcat 主页面中 2.准备工作 [1].安装tomcat [r ...
- css中渐变的分割线和自定义滚动条样式
css中渐变的分隔线: <div style="background:linear-gradient(to left,#efefef,#b6b6b6,#efefef);height:1 ...
- CorelDRAW常用工具之橡皮擦工具
很多作图以及设计软件都会自带橡皮擦工具,但对于专业做平面设计的小伙伴来说,普通的橡皮工具肯定是无法满足日常作图需求的,今天来看看CorelDRAW的橡皮擦能玩出什么花样来. 1.擦除对象 CorelD ...
- Camtasia快捷键大全
Camtasia是一款专业屏幕录制软件,它能在任何颜色模式下轻松地记录屏幕动作,另外它还具有即时播放和编辑压缩的功能.在生活上应用范围相当的广泛.在实际运用中如果能了解到相关的快捷键知识,相信是一定程 ...
- ucore操作系统学习(五) ucore lab5用户进程管理
1. ucore lab5介绍 ucore在lab4中实现了进程/线程机制,能够创建并进行内核线程的调度.通过上下文的切换令线程分时的获得CPU,使得不同线程能够并发的运行. 在lab5中需要更进一步 ...