洛谷P4502 [ZJOI2018]保镖(计算几何+三维凸包)
题面
题解
我对计蒜几盒一无所知
顺便\(xzy\)巨巨好强
前置芝士
三维凸包
啥?你不会三维凸包?快去把板子写了->这里
欧拉公式
\(V:vertex\)顶点,\(E:edge\)边,\(F:flat\)面,对所有维度的所有多边形(多面体)都成立
圆的反演
设反演中心为\(O\),常数为\(k\),若经过\(O\)的直线经过\(P,P'\),且\(OP\times OP'=k\),则称\(P,P'\)关于\(O\)互为反演,其中\(O\)为反演中心,\(k\)为反演幂
\(Voronoi\)图
又称泰森多边形,即划分一个平面,把每个平面划分到离它最近的点那里
\(Delaunay\)三角剖分
差不多就是类似于
不难发现,\(Voronoi\)图和\(Delaunay\)三角剖分互为对偶图
而且\(Delaunay\)三角剖分有一些奇特的性质
定理:三角形个数和和外围凸包上的点数之和为\(2n-2\)
用欧拉公式来证明,我们可以把这个三角剖分看成一个多面体,其中每一个三角形都是它的一个面,凸包上的所有点构成了底面,那么三角形个数就是\(F-1\)。设凸包上的点的个数为\(k\),那么有
化简之后发现有\(k+F-1=2n-2\)
顺带一提这个公式对所有的三角剖分都适用
定理:平面上的点集有唯一的\(Delaunay\)三角剖分(因为泰森多边形的每一个交点都属于三个顶点。唯一的例外就是存在四点共圆的时候,此时这个性质不成立)
定理:任意一个\(Delaunay\)三角形的外接圆不包含点集中的其他点。(称为\(Delaunay\)三角形的空圆性质)
\(First\)
凸包点数具有整体性,很难求,那么我们就可把它转化为求\(Delaunay\)三角形的期望个数
每一个\(Delaunay\)三角形都对应一个外接圆,我们称之为\(Delaunay\)圆,那么又变成数\(Delaunay\)圆的期望个数,设为\(k\),那么答案就是\(2n-2-k\)
我们定义支配圆为包含点集中所有点的圆,因为\(Delaunay\)圆不包含除了\(Delaunay\)三角形之外的任何一个点,所以有以下结论
定理:对于\(Delaunay\)圆,如果反演中心在这个圆中,那么在反演之后的图形中这个圆是支配圆,否则这个圆仍然是\(Delaunay\)圆
定理:对于支配圆,如果反演中心在这个圆中,那么在反演之后的图形中这个圆是\(Delaunay\)圆,否则它仍为支配圆
证明我不会,感性理解一下好了
那么题目就可以转化为求原图中\(Delaunay\)圆的个数\(\times\)反演中心在圆内的概率\(+\)支配圆的个数$\times \(反演中心在圆外的概率,我们设为\)E$
只要用\(Delaunay\)圆的总数加上支配圆的总数减去\(E\)就可以求得\(Delaunay\)三角形的期望个数了
\(Second\)
我们考虑一个圆的方程
如果我们设一个三维上的平面方程,为
那么点\((x,y)\)在圆上当且仅当\(z=x^2+y^2\)
所以我们可以把一个点\((x,y)\)投影到三维坐标系中,新的坐标设为\((x,y,x^2+y^2)\)。那么对于一个圆对应的平面\(z+Dx+Ey+F=0\)来说,如果一个点的坐标在这个平面上,那么这个点在这个圆上。同时也能发现如果点的坐标在平面下方,这个点在圆内,如果点的坐标在平面上方,这个点在圆外
那么我们对于所有的这个三维空间内的所有的点求一个凸包,然后我们发现,不过是\(Delaunay\)圆还是支配圆,它们对应的平面都只会在这个凸包的表面上
更进一步,我们发现下凸壳上的每一个平面都对应一个\(Delaunay\)圆,上凸壳上的每一个平面都对应一个支配圆
因为这\(n\)个顶点每个点都至少是一个\(Delaunay\)圆的顶点,所以每一个点都会存在于这个凸包中,那么这个凸包的点数就是\(n\),根据三维凸包里我们的计算,此时\(F=2n-4\),即面数为\(2n-4\),\(Delaunay\)圆的总数加上支配圆的总数就是\(2n-4\)
所以最终的答案可以表示成\(Ans=2n-2-(2n-4-E)=2+E\)
综上
我们读入所有点之后随机扰动来防止四点共圆和三维凸包中四点共面的情况。求出三维凸包之后,对于每个面,求出其对应的三个点的外接圆,如果是上凸面,则其为支配圆,只有在反演中心在圆内的时候才有贡献。如果是下凸面,则其为\(Delaunay\)圆,只有在反演中心在圆外时才有贡献。只要求出矩形和圆的交的面积就行了
然后兴高采烈地交上去发现精度爆炸只有\(60\)分
于是你需要一个叫做\(\_\_float128\)的黑科技
然后没有然后了
吉司机怕是根本没打算给部分分
//minamoto
#include<bits/stdc++.h>
#define R register
#define db __float128
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=2005;const db eps=1e-10;
inline db Rd(){return 1.0*rand()/RAND_MAX;}
inline db reps(){return (Rd()-0.5)*eps;}
inline int dcmp(const R db &x){return x<-eps?-1:x>eps?1:0;}
struct point{
db x,y;
point(){}
point(R db xx,R db yy):x(xx),y(yy){}
inline point operator -(const point &b)const{return point(x-b.x,y-b.y);}
inline point operator +(const point &b)const{return point(x+b.x,y+b.y);}
inline point operator *(const db &b)const{return point(x*b,y*b);}
inline point operator /(const db &b)const{return point(x/b,y/b);}
inline db operator *(const point &b)const{return x*b.y-y*b.x;}
inline db operator ^(const point &b)const{return x*b.x+y*b.y;}
inline db len(){return sqrt((double)(x*x+y*y));}
inline db len2(){return x*x+y*y;}
inline void rot(){R db t=x;x=-y,y=t;}
}qwq[4];
struct node{
db x,y,z;
node(){}
node(R db xx,R db yy,R db zz):x(xx),y(yy),z(zz){}
inline void shake(){x+=reps(),y+=reps(),z+=reps();}
inline node operator -(const node &b)const{return node(x-b.x,y-b.y,z-b.z);}
inline node operator *(const node &b)const{return node(y*b.z-z*b.y,z*b.x-x*b.z,x*b.y-y*b.x);}
inline db operator ^(const node &b)const{return x*b.x+y*b.y+z*b.z;}
}p[N];
struct Flat{
int id[3];
Flat(){}
Flat(R int x,R int y,R int z){id[0]=x,id[1]=y,id[2]=z;}
inline node normal(){return (p[id[1]]-p[id[0]])*(p[id[2]]-p[id[0]]);}
inline bool ck(const node &b){return ((b-p[id[0]])^normal())>0;}
}f[8005];
int n,cnt,x,y;
namespace Convex{
bool vis[N][N];Flat st[8005];int top,v;
void MAIN(){
fp(i,1,n)p[i].shake();
f[++cnt]=Flat(1,2,3),f[++cnt]=Flat(3,2,1);
fp(i,4,n){
top=0;
fp(j,1,cnt){
v=f[j].ck(p[i]),!v?(st[++top]=f[j],0):0;
vis[f[j].id[0]][f[j].id[1]]=
vis[f[j].id[1]][f[j].id[2]]=
vis[f[j].id[2]][f[j].id[0]]=v;
}
fp(j,1,cnt){
if(vis[f[j].id[0]][f[j].id[1]]&&!vis[f[j].id[1]][f[j].id[0]])st[++top]=Flat(i,f[j].id[0],f[j].id[1]);
if(vis[f[j].id[1]][f[j].id[2]]&&!vis[f[j].id[2]][f[j].id[1]])st[++top]=Flat(i,f[j].id[1],f[j].id[2]);
if(vis[f[j].id[2]][f[j].id[0]]&&!vis[f[j].id[0]][f[j].id[2]])st[++top]=Flat(i,f[j].id[2],f[j].id[0]);
}
fp(j,1,top)f[j]=st[j];cnt=top;
}
}
}
namespace Area{
point cross(R point a1,R point a2,R point b1,R point b2){
R point a=a2-a1,b=b2-b1,c=b1-a1;
R db t=(b*c)/(b*a);
return a1+a*t;
}
inline db rad(const R point &p1,const R point &p2){return atan2((double)(p1*p2),(double)(p1^p2));}
//这里计算角度有方向,表示p2顺时针旋转多少度到p1
db calc(R db r,R point p1,R point p2){
R point e=(p1-p2)/(p1-p2).len(),ee=e;e.rot();
R point mid=cross(p1,p2,point(0,0),e);
if(mid.len()>=r)return r*r*rad(p1,p2)/2;
R db d=sqrt((double)(r*r-mid.len2()));
R point w1=mid+ee*d,w2=mid-ee*d;
R int b1=(dcmp(p1.len2()-r*r)>0),b2=(dcmp(p2.len2()-r*r)>0);
if(b1&&b2){
if(dcmp((p1-mid)^(p2-mid))<=0)return r*r*(rad(w2,p2)+rad(p1,w1))/2+w1*w2/2;
return r*r*rad(p1,p2)/2;
}
if(b1)return (r*r*rad(p1,w1)+w1*p2)/2;
if(b2)return (r*r*rad(w2,p2)+p1*w2)/2;
return p1*p2/2;
}
db ins(R point O,R db r){
return calc(r,qwq[0]-O,qwq[1]-O)
+calc(r,qwq[1]-O,qwq[2]-O)
+calc(r,qwq[2]-O,qwq[3]-O)
+calc(r,qwq[3]-O,qwq[0]-O);
}
}
namespace loli{
db s,res,r;
point a1,a2,b1,b2,c,d,O;
void MAIN(){
n=read(),x=read(),y=read(),qwq[0]=point(x,y);
qwq[1].y=y,qwq[3].x=x;
x=read(),y=read(),qwq[2]=point(x,y);
qwq[1].x=x,qwq[3].y=y;
s=(qwq[2].x-qwq[0].x)*(qwq[2].y-qwq[0].y),res=0;
fp(i,1,n)x=read(),y=read(),p[i]=node(x,y,1.0*x*x+1.0*y*y);
Convex::MAIN();
fp(i,1,cnt){
a1=point(p[f[i].id[0]].x,p[f[i].id[0]].y),
a2=point(p[f[i].id[1]].x,p[f[i].id[1]].y),
c=(a1+a2)/2,d=a2-a1,d.rot(),a1=c,a2=c+d,
b1=point(p[f[i].id[1]].x,p[f[i].id[1]].y),
b2=point(p[f[i].id[2]].x,p[f[i].id[2]].y),
c=(b1+b2)/2,d=b2-b1,d.rot(),b1=c,b2=c+d,
O=Area::cross(a1,a2,b1,b2),
d=point(p[f[i].id[0]].x,p[f[i].id[0]].y),
r=(O-d).len();
f[i].normal().z>0?res+=s-Area::ins(O,r):res+=Area::ins(O,r);
}
printf("%.11lf\n",(double)(res/s+2));
}
}
int main(){
srand(19260817);
// freopen("testdata.in","r",stdin);
loli::MAIN();
return 0;
}