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. Excel 查找替换 -- 快速填充

    单元格匹配,这样就不会把 70 的 0 替换成 7零 了 Ctrl + E 快速填充 一列变多行 快速填充 1. 快速拆分数据 一列数据中包含了姓名和手机号码,这时你需要进行数据拆分,快速填充可以实现 ...

  2. .NET Moq mock internal类型

    问题 Can not create proxy for type xxx because type xxx is not accessible. Make it public, or internal ...

  3. 地图区域大数据量 marker 坐标点高效抽稀算法

    按网上的思路一般要写双层循环,第一层循环遍历点集合,时间复杂度为O(N),第二层循环遍历结果集,逐一计算距离,距离小于阈值的不加入结果集,距离大于阈值的加入结果集,时间复杂度为O(M),双层循环总时间 ...

  4. 题解 CF1388A 【Captain Flint and Crew Recruitment】(思维、贪心)

    AC代码: #include<bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; if (n ...

  5. JXUST_NC - ACM工作室20级选拔赛题解

    A - RioTian学长的星际航线 并查集板子 #include <bits/stdc++.h> using namespace std; const int maxn = 1010; ...

  6. 什么是全同态加密(FHE)中的自举(Bootstrapping)?

    PrimiHub一款由密码学专家团队打造的开源隐私计算平台,专注于分享数据安全.密码学.联邦学习.同态加密等隐私计算领域的技术和内容. 全同态加密(Fully Homomorphic Encrypti ...

  7. CSS Sticky Footer 几种实现方式

    项目里,有个需求,登录页,信息,需要使用到sticky footer布局,刚好,巩固下这个技术: 核心代码: 播客: https://www.jb51.net/css/676798.html 视频:h ...

  8. 新手学习VUE——环境搭建及创建项目

    第一种方式: 1.     下载安装node.js 检查是否成功:node-v或npm-v 2..搭建项目: 第一种方法:用iview脚手架建项目 打开iview官网==>生态 ===>i ...

  9. idea 配置 service 服务,多模块同时启动

    转载请注明出处: 1,打开IDEA项目 .idea 下 的workspace.xml 2,查找"RunDashboard" 节点 3,添加如下内容 <option name= ...

  10. 使用VS开发人员工具观察类在内存中的布局

    1.先要生成相应文件 2.打开VS2019开发人员工具 3.cd至文件目录 4.输入cl /d1 reportSingleClassLayoutanimal demo.cpp 其中reportSing ...