Problem Link

给定 \(n\) 个球和一个点 \(P\),求点 \(P\) 到这些球的交内一点的距离的最小值。保证有解。\(n\le 10^6\)。


和最小圆覆盖一个套路。考虑维护一个当前答案,初始即为询问点 \(P\)。

从 \(1\) 到 \(n\) 枚举 \(i\),如果当前答案在球 \(i\) 内则跳过,否则可以证明答案一定在 \(i\) 的球面上。证明后面再说。

做法是:直 remake,将当前答案设为 \(P\) 到球 \(i\) 表面的最小距离点,然后枚举 \(j=1\sim i-1\),和上面一样,如果当前答案在球 \(j\) 内,那无所谓;否则,同理可以说明答案也一定在 \(j\) 的球面上(注意此时答案在 \(i\) 的球面上的前提仍然保持),所以推出答案在球 \(i\) 和球 \(j\) 相交所得的圆上,找出 \(P\) 到这个圆的距离作为最新答案。然后继续枚举 \(k=1\sim j-1\),同理如果当前答案不在球 \(k\) 内则更新为 \(i,j,k\) 三个球的两个交点之中离 \(P\) 较近的那个。

首先来考虑正确性。当我们枚举 \(i\) 发现出问题的时候,由于球交是凸的,所以原先答案到此时的答案(此时的答案定义为 \(1\sim i\) 中某 3 个球的交点的最优解)一定存在一条连续的移动路径使得移动过程中和 \(P\) 的距离始终不变,于是最优解一定在 \(i\) 的球面上(而不是球内)

然后枚举 \(j\),当前答案定义为“考虑 \(i\) 和 \(\le j\) 的某两个球交出的点的最优答案”,那么如果 \(j\) 不包含最优解,同上是可以移动到 \(j\) 的球面上的。\(k\) 同理。

至于为什么答案一定可以由 \(1\sim 3\) 个球的球交中的最优解得到,可以考虑最终答案一定在某个“犄角旮旯”,那里是由至多 3 个球交出来的。

那么时间复杂度呢?和最小圆覆盖一样分析,开始的时候随机重排所有点,

对于单个 \(j\),枚举 \(k\) 的时间复杂度是 \(O(j)\) 的。对于单个 \(i\),考虑每个 \(1\le j<i\),\(j\) 能更新答案,意味着 \(j\) 是 “\(i\) 和 $1\sim j $ 中某两个球能组成答案的最优解”所取到的那两个球之一,这个概率是 \(2/j\)!所对单个 \(i\) 时间复杂度是 \(\sum_{j=1}^{i-1}O(j)\cdot 2/j =O(i)\)。用完全相同的分析可以得到总时间复杂度为 \(O(n)\)。

点击查看代码
#include <bits/stdc++.h>
#define For(i,a,b) for(int i=a;i<=b;i++)
#define Rev(i,a,b) for(int i=a;i>=b;i--)
#define Fin(file) freopen(file,"r",stdin);
#define Fout(file) freopen(file,"w",stdout);
using namespace std;
const int N=1e6+5; using ll = long long; const double EPS=1e-6;
struct Point{
double x,y,z;
Point(double xx=0,double yy=0,double zz=0) : x(xx),y(yy),z(zz) {}
};
typedef Point Vector;
inline Vector operator- (Point A,Point B) { return Vector(A.x-B.x,A.y-B.y,A.z-B.z); }
inline Point operator+ (Point A,Vector v) { return Point(A.x+v.x,A.y+v.y,A.z+v.z); }
inline Vector operator* (Vector v,double o) { return Vector(v.x*o,v.y*o,v.z*o); }
inline Vector operator/ (Vector v,double o) { return Vector(v.x/o,v.y/o,v.z/o); }
inline double Length2(Vector v) { return v.x*v.x+v.y*v.y+v.z*v.z; }
inline double Dist(Point U,Point V) { return sqrt(Length2(U-V)); }
inline double Dot(Vector a,Vector b) { return a.x*b.x+a.y*b.y+a.z*b.z; }
inline Vector Cross(Vector a,Vector b) { return Vector(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x); }
inline Point Go(Point A,Point B,double d) { return A+(B-A)*d/Dist(A,B); }
inline int dcmp(double d) { return d<-EPS?-1:d>EPS?1:0; }
int n; Point O[N]; double R[N]; mt19937 rng(190345);
inline bool In(int u,Point A) { return Dist(O[u],A)<=R[u]; }
inline double Cos(double a,double b,double c) { return (a*a+b*b-c*c)/(2*a*b); }
inline Point Perp(Point T,Vector v,Point X){
auto [A,B,C]=X-T; auto [a,b,c]=v;
double p=-(a*A+b*B+c*C)/(a*a+b*b+c*c);
return X+v*p;
}
inline Point solve1(Point U,double R1,Point P) { return Go(U,P,R1); }
inline Point solve2(Point U,Point V,double R1,double R2,Point P){
double d=Dist(U,V);
Point T=Go(U,V,R1*Cos(d,R1,R2));
Point S=Perp(T,V-U,P);
Point K=Go(T,S,R1*sin(acos(Cos(d,R1,R2))));
return K;
}
inline Point solve3(Point U,Point V,Point W,double R1,double R2,double R3,Point P){
double d=Dist(U,V);
Point T=Go(U,V,R1*Cos(d,R1,R2));
Point S=Perp(T,V-U,W);
Point K=Go(T,S,R1*sin(acos(Cos(d,R1,R2))));
double r1=Dist(T,K),r2=sqrt(R3*R3-Length2(W-S));
Point X=Go(T,S,r1*Cos(Dist(S,T),r1,r2));
Vector v=Cross(V-U,W-U); v=v/sqrt(Length2(v));
double h=sqrt(r1*r1-Length2(T-X));
Point Y=X+v*h,Z=X-v*h;
return Dist(Y,P)<Dist(Z,P)?Y:Z;
}
int main(){
//~ Fin("ball.in"); Fout("ball.out");
ios::sync_with_stdio(0); cin.tie(0);
cout<<setprecision(6)<<fixed;
Point P; cin>>n>>P.x>>P.y>>P.z; For(i,1,n) cin>>O[i].x>>O[i].y>>O[i].z>>R[i];
For(i,2,n) { int j=rng()%i+1; swap(O[i],O[j]); swap(R[i],R[j]); }
Point Ans=P;
For(i,1,n) if(!In(i,Ans)) {
Ans=solve1(O[i],R[i],P);
For(j,1,i-1) if(!In(j,Ans)){
Ans=solve2(O[i],O[j],R[i],R[j],P);
For(k,1,j-1) if(!In(k,Ans)) {
Ans=solve3(O[i],O[j],O[k],R[i],R[j],R[k],P);
}
}
}
cout<<Ans.x<<' '<<Ans.y<<' '<<Ans.z<<'\n';
return 0;
}

【计算几何,数学】7.14 T3 @ xdfz的更多相关文章

  1. Rightmost Digit(快速幂+数学知识OR位运算) 分类: 数学 2015-07-03 14:56 4人阅读 评论(0) 收藏

    C - Rightmost Digit Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit ...

  2. POJ 1113 Wall(思维 计算几何 数学)

    题意 题目链接 给出平面上n个点的坐标.你需要建一个围墙,把所有的点围在里面,且围墙距所有点的距离不小于l.求围墙的最小长度. \(n \leqslant 10^5\) Sol 首先考虑如果没有l的限 ...

  3. 2019.2.14 t3 车辆销售

    用算法求最大生成树,在并查集合并时,把原本的一个根连向另一个 根改成两个根都连向一个新建的节点,并把当前正在处理的边的权值赋给这个新 节点做点权.这样形成的结构会是一棵树. 一个点的答案大致上是树的根 ...

  4. JZOJ 11.14 提高B组反思

    JZOJ 11.14 提高B组反思 T1 题目虽然有点高大上,但是很容易懂 有一个\(d\)维空间,同时有一个长度为\(2n\)的操作序列,每个操作往某一维的正方向或反方向走一格,问多少种方案使得最后 ...

  5. 2017/10 冲刺NOIP集训记录:暁の水平线に胜利を刻むのです!

    前几次集训都没有记录每天的点滴……感觉缺失了很多反思的机会. 这次就从今天开始吧!不能懈怠,稳步前进! 2017/10/1 今天上午进行了集训的第一次考试…… 但是这次考试似乎是近几次我考得最渣的一次 ...

  6. Diary -「CSP 2019 J/S」 游记

    \(\text{Day 0}\) 试机, 总体感觉不错, 至少不像初一时候的紧张, 毕竟是中青年选手了 ( ? )         当晚睡得挺好, 虽然是冲着一等奖去的, 但还是没有给自己过多的思想包 ...

  7. SQL函数汇总【精选篇】

    1.绝对值   SQL:select abs(-1) value  O:select abs(-1) value from dual  2.取整(大)   S:select ceiling(-1.00 ...

  8. Python之路【第四篇】:模块

    什么是模块: 模块就是一个功能的集合. 模块就和乐高积木差不多,你用这些模块组合出一个模型,然后也可以用这个模块加上其他的模块组合成一个新的模型 模块的种类: 1.内置模块(python自带的比如os ...

  9. Oracle/Mysql/SqlServer函数区别

    mysql日期和时间格式转换 Linux scp 使用详解 Oracle/Mysql/SqlServer函数区别 2011-07-01 12:34:36|  分类: Mysql技术 |  标签:mys ...

  10. SQL(Oracle)日常使用与不常使用函数的汇总

    --日常使用的sql语句和oracle语句,有些相对使用的频率比较高,收藏起来还是比较值得的 -- 绝对值 SQL:) value Oracle:) value from dual -- 2.取整(大 ...

随机推荐

  1. 如何用Xcode安装ipa

    Xcode安装ipa iOS APP上架App Store其中一个步骤就是要把ipa文件上传到App Store!​ 下面进行步骤介绍!​ 利用Appuploader这个软件,可以在Windows.L ...

  2. Solon 开发进阶,三、常用配置说明

    Solon 开发进阶 一.插件扩展机制 二.体外扩展机制 三.常用配置说明 四.启动参数说明 五.全局异常订阅 应用主配置文件为: resources/app.yml( 或 app.propertie ...

  3. allowedOrigins cannot contain the special value "*"

    Spring Boot的版本高于 2.4以后 ,原来的配置已经不适合目前的版本 将代码中的allowedOrigins改为allowedOriginPatterns @Configuration pu ...

  4. 详解 SSL(一):网址栏的小绿锁有什么意义?

    随着互联网的飞速发展,用户信息泄漏.数据泄露等安全问题的事件频繁发生.这一切不一定是网站的问题,有时候可能是自己不经意间泄露了自己的信息.例如钓鱼网站就是日常生活中比较常见的,钓鱼网站和真实网站差别细 ...

  5. AliSSR 语音超分算法:让在线会议语音更明亮更自然

    超分让在线会议语音更明亮,在线会议已成为日常工作中较为普遍的沟通交流方式,接入会议的方式也呈现多样化,比如电脑入会.手机入会又或是电话入会. 雪雅.曜辰|作者 众所周知,高采样率且高带宽的音频信号富含 ...

  6. MMSC 扩充物料库存地点

    当涉及到物料的库存地点时,系统通常会做校验,该物料是否扩充了库存地点,没有扩充则报错.为了不使这样的错误干扰到程序逻辑,通常会在涉及时,先查询MARD表,判断是否存在对应的库存地点.如果没有存在,则直 ...

  7. 使用触发器和C#程序实现数据同步

    项目中用到了Quartz,也算是Quartz的一个示例. 源库创建数据同步表SYNC_DATA: CREATE TABLE SYNC_DATA( ID VARCHAR2(36) NOT NULL, T ...

  8. 数字孪生 3D 风电场,智慧风电之陆上风电

    前言 "十四五"期间,在传统产业数字化升级和绿色改造领域.绿色低碳城镇化和现代城市建设领域.绿色低碳消费领域,和可再生能源或电力系统建设等领域,总投资可以达到近 45 万亿,平均每 ...

  9. 3、springboot连接数据库

    系列导航 springBoot项目打jar包 1.springboot工程新建(单模块) 2.springboot创建多模块工程 3.springboot连接数据库 4.SpringBoot连接数据库 ...

  10. 如何使用单纯的`WebAssembly`

    一般来说在.net core使用WebAssembly 都是Blazor ,但是Blazor渲染界面,.net core也提供单纯的WebAssembly这篇博客我将讲解如何使用单纯的WebAssem ...