题目链接:
http://hihocoder.com/contest/challenge14/problem/3
题意:
给定n对点,从每对点中等概率选出1个点,得到n个点,求凸包面积的期望。
分析:
凸包的面积可以对每条边的两个顶点用叉积进行计算,相当于将凸包划分为若干个有向三角形,
根据期望的线性可加性,可以分别枚举每一条有向边,计算这条边是凸包上的一条边的概率,
那么这条边对期望的贡献就是概率乘上这条边对原点的有向三角形的面积,
这里规定凸包的走向是逆时针的,
考虑来自不同点对的P和Q,那么选中PQ的概率是1/4,
那么有向线段PQ能作为凸包上的一条边,当且仅当PQ的逆时针方向没有被选中的点,
于是可以枚举另外的n-2对点,
如果存在某一对点的两个点均在PQ的逆时针方向,则这条边在凸包上的概率为0,
否则,如果有一个点在PQ的逆时针方向,则必须选另外一个点,概率要乘上1/2,
直接枚举线段再枚举点对复杂度是O(n^3)的,可以利用极角扫描优化到O(n^2*logn)。
代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef double db;
const int MAXN=1005;
const db PI=acos(-1.0);
struct Point
{
db x,y;
Point(){}
Point(db _x,db _y):x(_x),y(_y){}
Point operator - (const Point &t)const
{
return Point(x-t.x,y-t.y);
}
db operator * (const Point &t)const
{
return x*t.y-y*t.x;
}
}p[MAXN<<1];
vector<pair<db,int> >v;
int mp[MAXN];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lf%lf",&p[i<<1].x,&p[i<<1].y);
scanf("%lf%lf",&p[i<<1|1].x,&p[i<<1|1].y);
}
db ans=0;
for(int ct=0;ct<2*n;ct++)
{
v.clear();
for(int i=0;i<2*n;i++)
{
if(i==ct || (i^1)==ct)continue;
Point tp=p[i]-p[ct];
db arc=atan2(tp.y,tp.x);
if(arc<0)arc+=2*PI;
v.push_back(make_pair(arc,i));
}
sort(v.begin(),v.end());
int tot=v.size();
for(int i=0;i<tot;i++)
{
v.push_back(make_pair(v[i].first+2*PI,v[i].second));
}
memset(mp,0,sizeof(mp));
int fcnt=0,scnt=0,r=0;
for(int i=0;i<tot;i++)
{
while(r<i+tot && v[r].first-v[i].first<PI)
{
mp[v[r].second/2]++;
if(mp[v[r].second/2]==2)
{
scnt++;
fcnt--;
}
else fcnt++;
r++;
}
mp[v[i].second/2]--;
if(mp[v[i].second/2]==1)scnt--;
else fcnt--;
if(!scnt)
ans+=p[v[i].second]*p[ct]*pow(0.5,fcnt)/2.0;
if(mp[v[i].second/2]==1)fcnt++;
}
}
printf("%.10f\n",ans/4.0);
return 0;
}