【hihocoder】#1225 : 向日葵-凸包&极角扫描

传送门:hihocoder1225


题解

把每个凸包的面积三角剖分,划分成每个三角形的有向面积之和。
转化成统计每条边作为凸包边的概率 × \times ×该边与基点构成三角形有向面积。

假设凸包边为逆时针指向,所以对于当边 i , j i,j i,j右侧没有点时,该边即为凸包边。若存在一组的两个点都在 i , j i,j i,j右侧,则概率为 0 0 0,否则概率为 1 2 一 组 中 有 一 个 点 在 i , j 右 侧 的 组 数 \frac 12 ^{一组中有一个点在i,j右侧的组数} 21i,j,最后还要乘上选中 i , j i,j i,j的概率为 1 4 \frac 14 41

枚举起点,极角扫描将复杂度优化到 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)


代码

这份代码被卡精度了QWQ。卡不动弃疗了。
算几想题一分钟,写题写一天

#include
typedef double db;
using namespace std;
const db pi=acos(-1.0);
const int N=2100;

int n,m,cnt[N],cot,num;
db ans,pw[N];

struct P{
   db x,y;
   P(db x_=0.0,db y_=0.0):x(x_),y(y_){};
   inline P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
   inline P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
   inline db operator ^(const P&ky){return x*ky.y-y*ky.x;}
}p[N],st;

struct Q{
	P pt;db ag;int id;
	Q(){};
	Q(P pt_,int id_){pt=pt_;id=id_;ag=atan2(pt.y,pt.x);}
    bool operator<(const Q&ky)const{return ag<ky.ag;}	
}q[N];

inline void ins(int x){if((++cnt[x])==2) cot++;num++;}
inline void del(int x){if((--cnt[x])==1) cot--;num--;}
inline db cal(int x,int op)
{
	int i,j,pos;db re=0.0,res;
	st=p[x+op*n];m=cot=num=0;memset(cnt,0,sizeof(cnt));
	for(i=1;i<=n;++i) if(i^x) q[++m]=Q(p[i]-st,i),q[++m]=Q(p[i+n]-st,i);
	sort(q+1,q+m+1);
	for(j=m;j>0 && q[j].ag>0;--j) ins(q[j].id);pos=++j;
	for(i=1;i<=m && q[i].ag<=0;++i){
		for(;j<=m && q[j].ag-q[i].ag<=pi;++j) del(q[j].id);
		if(!cot) re+=(st^(q[i].pt+st))*pw[num-cnt[q[i].id]];
		ins(q[i].id); 
	}
    for(;j<=m;++j) del(q[j].id);j=1;
	for(i=pos;i<=m;++i){
		for(;j<pos && q[i].ag-q[j].ag>=pi;++j) del(q[j].id);
		if(!cot) re+=(st^(q[i].pt+st))*pw[num-cnt[q[i].id]];
		ins(q[i].id); 
	}
	return re;
}

int main(){
	int i,j,x,y;
	scanf("%d",&n);pw[0]=1.0;
	for(i=1;i<=n;++i){
		scanf("%d%d",&x,&y);p[i]=P(x,y);
		scanf("%d%d",&x,&y);p[i+n]=P(x,y);
		pw[i]=pw[i-1]*0.5;
	}
	for(i=1;i<=n;++i) ans+=cal(i,0)+cal(i,1);
	printf("%.10lf",(double)(ans/8.0));
	return 0;
}

你可能感兴趣的:(凸包)