【ZJOI2018】保镖(Voronoi图)(Delaunay三角剖分)(圆的反演)(三维凸包)(欧拉定理)

传送门


看到这篇题解请先大吼三声:杜教NB!杜教NB!杜教NB!

在这里插入图片描述

其实话说回来,吉老师改不改数据范围其实都差不多。。。

反正中间的部分分估计也没有人写

如果吉老师不改数据范围的话,估计现在网上也就能找到单 l o g log 的三维凸包做法了。我仍然不会单log三维凸包

题解:

对于一个大小为 n n 的点集,任何一个三角剖分,其外围点数量和三角形数量之和 2 n 2 2n-2 。证明考虑将其映射为三维图形,然后用拓扑学欧拉定理证明就行了。

或者直接考虑利用平面图欧拉定理(虽然本质上是一个东西),我们有 V E + F = 2 V-E+F=2

设凸包上点数为 k k ,显然三角形的数量就是面数扣掉那个无限大区域,即 F 1 F-1 ,点数为 n n ,凸包上每条边算了两次,三角形上每条边算了三次,则

n ( ( F 1 ) 3 + k ) / 2 + F = 2 n-((F-1)*3+k)/2+F=2

化简就是 k + F 1 = 2 n 2 k+F-1=2n-2

对于凸包而言,显然要求作出的三角剖分是凸的就行了。

所以我们可以考虑计算在三角剖分外围是凸的情况下,内部有多少个三角形。

容易注意到题目给的条件是反演的形式,这启示我们采用反演相关的知识来解决。

与反演关系最紧密的是圆,与圆关系最紧密的三角剖分就是D剖。

定义内圆为经过点集中至少三个点且内部没有其他点的圆,外圆为经过点集中至少三个点且外部没有其他点的圆。

容易发现内圆可以直接用最近点V图求,也就是直接上D剖。而外圆过的三个点对应的在最远点V图中相邻的三个点。这个目前不清楚有没有快速的直接求法

考虑反演前后的图形关系,显然可以得到:

  1. 对于内圆,若反演中心在其内部,其反形为外圆,否则其仍然为内圆。
  2. 对于外圆,若反演中心在其内部,其反形为内圆,否则其仍然为外圆。

反演中心在边界上的概率为 0 0 ,不考虑。

所以我们求出所有的内圆外圆,然后与原矩形求交即可。

由于外圆没法求,似乎陷入了僵局。

看一下圆的方程: ( x x 0 ) 2 + ( y y 0 ) 2 = R 2 (x-x_0)^2+(y-y_0)^2=R^2

拆开是这样的形式: x 2 + y 2 + A x + B y + C = 0 x^2+y^2+Ax+By+C=0

三维坐标系中的平面方程为 z + A x + B y + C = 0 z+Ax+By+C=0

考虑将二维平面的点 ( x , y ) (x,y) 映射到三维 ( x , y , x 2 + y 2 ) (x,y,x^2+y^2) ,这样每三个点所在的平面可以表示一个圆。接下来要考虑如何表示圆和点的关系。显然,若点在该平面下方,该点在圆内,否则在圆外。

所以直接求三维凸包,面向 z z 轴负方向的表示内圆,面向正方向的表示外圆。

为了避免与 z z 轴平行的情况出现,加一点随机扰动即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define db long double
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
	}template<typename T>T get_integer(){
		char c;T num;while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=((num+(num<<2))<<1)+(c^48);return num;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;

std::mt19937 R(time(0));
std::uniform_int_distribution<int> rnd(0,(int)1e9);
cs db eps=5e-8;
inline db rndb(){return rnd(R)/1e10;}
inline db reps(){return (rndb()-.5)*eps;}

inline int sign(db x){return x<-eps?-1:(x>eps?1:0);}
struct Pnt{
	db x,y;Pnt(){}Pnt(db _x,db _y):x(_x),y(_y){}
	friend Pnt operator+(cs Pnt &a,cs Pnt &b){return Pnt(a.x+b.x,a.y+b.y);}
	friend Pnt operator-(cs Pnt &a,cs Pnt &b){return Pnt(a.x-b.x,a.y-b.y);}
	friend db operator*(cs Pnt &a,cs Pnt &b){return a.x*b.y-b.x*a.y;}
	friend db dot(cs Pnt &a,cs Pnt &b){return a.x*b.x+a.y*b.y;}
	friend Pnt operator*(cs Pnt &a,db b){return Pnt(a.x*b,a.y*b);}
	db len()cs{return sqrtl(x*x+y*y);}db ang()cs{return atan2l(y,x);} 
};

struct pt{
	db x,y,z;pt(){}pt(db _x,db _y,db _z):x(_x),y(_y),z(_z){}
	friend pt operator+(cs pt &a,cs pt &b){return pt(a.x+b.x,a.y+b.y,a.z+b.z);}
	friend pt operator-(cs pt &a,cs pt &b){return pt(a.x-b.x,a.y-b.y,a.z-b.z);}
	friend pt operator*(cs pt &a,cs pt &b){return pt(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x);}
	friend db dot(cs pt &a,cs pt &b){return a.x*b.x+a.y*b.y+a.z*b.z;}
	friend bool operator==(cs pt &a,cs pt &b){return !sign(a.x-b.x)&&!sign(a.y-b.y)&&!sign(a.z-b.z);}
	friend bool operator!=(cs pt &a,cs pt &b){return sign(a.x-b.x)||sign(a.y-b.y)||sign(a.z-b.z);}
	db len()cs{return sqrtl(x*x+y*y+z*z);}
};

inline db crs(cs Pnt &a,cs Pnt &b,cs Pnt &c){
	return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); 
}
inline pt crs(cs pt &a,cs pt &b,cs pt &c){
	return (b-a)*(c-a);
}
inline pt trans(cs Pnt &a){
	return pt(a.x,a.y,a.x*a.x+a.y*a.y);
}
inline Pnt trans(cs pt &a){
	return Pnt(a.x,a.y);
}

inline db area(cs pt &a,cs pt &b,cs pt &c){
	return crs(a,b,c).len();
}
inline db vol(cs pt &a,cs pt &b,cs pt &c,cs pt &d){
	return dot(crs(a,b,c),d-a);
}

cs int N=2e3+7;

int n,ct,id[N][N];
bool ok[N<<2|1];

pt p[N];Pnt q[N];
db xa,xb,ya,yb,S;
Pnt poly[5],tmp[10];

struct Face{
	int a,b,c;Face(){}Face(int _a,int _b,int _c):a(_a),b(_b),c(_c){}
	pt normal()cs{return crs(p[a],p[b],p[c]);}
	db area()cs{return normal().len();}
	db vol(cs pt &d)cs{return dot(normal(),d-p[a]);}
}f[N<<2|1];

void dfs(int,int);
void deal(int,int,int);

void deal(int x,int a,int b){
	if(ok[id[a][b]]){
		if(f[id[a][b]].vol(p[x])>0)dfs(x,id[a][b]);
		else {
			id[b][a]=id[a][x]=id[x][b]=++ct;
			f[ct]=Face(b,a,x);ok[ct]=true;
		}
	}
}

void dfs(int p,int x){
	ok[x]=false;
	deal(p,f[x].b,f[x].a);
	deal(p,f[x].a,f[x].c);
	deal(p,f[x].c,f[x].b);
}

void deal(int p,int x){
	ok[x]=false;
	deal(p,f[x].b,f[x].a);
	deal(p,f[x].a,f[x].c);
	deal(p,f[x].c,f[x].b);
}

void Convex_3D(){
	ct=0;int flag=true;
	for(int re i=2;i<=n;++i)if(p[1]!=p[i])
	{std::swap(p[2],p[i]);flag=false;break;}
	if(flag)printf("%.20Lf",(db)1),exit(0);flag=true;
	for(int re i=3;i<=n;++i)if(sign(area(p[1],p[2],p[i])))
	{std::swap(p[3],p[i]);flag=false;break;}
	if(flag)printf("%.20Lf",(db)n),exit(0);flag=true;
	for(int re i=4;i<=n;++i)if(sign(vol(p[1],p[2],p[3],p[i])))
	{std::swap(p[4],p[i]);flag=false;break;}
	if(flag)printf("%.20Lf",(db)n),exit(0);
	
	for(int re i=1;i<=n;++i)
		p[i].x+=reps(),p[i].y+=reps(),p[i].z+=reps();
	for(int re i=1;i<=4;++i){
		int a=(i&3)+1,b=(a&3)+1,c=(b&3)+1;
		if(vol(p[a],p[b],p[c],p[i])>0)std::swap(b,c);
		id[a][b]=id[b][c]=id[c][a]=++ct;
		f[ct]=Face(a,b,c);ok[ct]=true;
	}
	for(int re i=5;i<=n;++i){
		for(int re j=1;j<=ct;++j)
			if(f[j].vol(p[i])>0)
				{dfs(i,j);flag=j;break;}
		if(!flag)continue;int cur=flag-1;flag=false;
		for(int re j=cur+1;j<=ct;++j)if(ok[j]){
			if((++cur)==j)continue;f[cur]=f[j],ok[cur]=true;
			id[f[cur].a][f[cur].b]=id[f[cur].b][f[cur].c]=id[f[cur].c][f[cur].a]=cur;
		}ct=cur;
	}
} 

inline db solve(db r,db x,db y){
	if(x<0||y<0)return (x*y<0)?-solve(r,fabsl(x),fabsl(y)):solve(r,fabsl(x),fabsl(y));
	if(x*x+y*y<=r*r)return x*y;db p=(r<y)?0:sqrtl(r*r-y*y),q=(r<x)?0:sqrtl(r*r-x*x);
	return (p*y+q*x+(atan2l(y,p)-atan2(q,x))*r*r)*0.5;
}
inline Pnt intersection(cs Pnt &a,cs Pnt &b,cs Pnt &c,cs Pnt &d){
	return a+(b-a)*(crs(c,d,a)/((b-a)*(d-c)));
}
inline bool on_seg(cs Pnt &p,cs Pnt &s,cs Pnt &e){return sign(dot(p-s,p-e))<=0;}

db calc(cs Face &f){
	int a=f.a,b=f.b,c=f.c;std::swap(b,c);
	if(fabsl(crs(q[a],q[b],q[c]))<eps){
		db res=0;int tot=0;
		if((crs(p[a],p[b],p[c]).z<0)^
			(dot(p[b]-p[a],p[c]-p[a])<0))
			std::swap(b,c);
		for(int re i=0;i<4;++i){
			if(crs(q[b],q[c],poly[i])>=-eps)tmp[tot++]=poly[i];
			if(fabsl((q[c]-q[b])*(poly[i]-poly[i+1]))>eps){
				Pnt p=intersection(q[b],q[c],poly[i],poly[i+1]);
				if(on_seg(p,poly[i],poly[i+1]))tmp[tot++]=p;
			}
		}for(int re i=1;i+1<tot;++i)res+=crs(tmp[0],tmp[i],tmp[i+1]);
		return fabsl(res*0.5);
	}pt P=crs(p[a],p[b],p[c]);
	db x=P.x/P.z*-.5,y=P.y/P.z*-.5,r=sqrtl((p[a].x-x)*(p[a].x-x)+(p[a].y-y)*(p[a].y-y));
	db xl=std::max(xa-x,-r),xr=std::min(xb-x,r),yd=std::max(ya-y,-r),yu=std::min(yb-y,r);
	if(xl>=xr||yd>=yu)return 0;
	return solve(r,-xl,-yd)+solve(r,xr,-yd)+solve(r,-xl,yu)+solve(r,xr,yu);
}

void Main(){
	n=gi();if(n==3)return (void)printf("3\n");
	xa=gi(),ya=gi(),xb=gi(),yb=gi();
	poly[0]=Pnt(xa,ya),poly[1]=Pnt(xb,ya);
	poly[2]=Pnt(xb,yb),poly[3]=Pnt(xa,yb);poly[4]=poly[0];
	S=(xb-xa)*(yb-ya);db s=(2*n-2)*S;
	for(int re i=1;i<=n;++i)
		q[i].x=gi(),q[i].y=gi(),p[i]=trans(q[i]);
	Convex_3D();
	for(int re i=1;i<=ct;++i)
		s-=(f[i].normal().z>0)?calc(f[i]):(S-calc(f[i]));
	printf("%.20Lf",s/S);
}

void file(){
#ifdef zxyoi
	freopen("guard.in","r",stdin);
#endif
}
signed main(){file();Main();return 0;} 
发布了967 篇原创文章 · 获赞 369 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/104038273