Description
N,M<=1e3
Solution
考虑每个点被观察到的概率
这样很难算我们可以计算每个点不被观察到的概率
这个等价于把这个点和所有观察点拉出来一起做凸包,这个点出现在凸包上的概率
那么我们可以枚举两条边,计算这两条边出现的概率
就是这两条边外部的点全部不出现
这些点把所有观察点关于这个点做极角排序后是连续的一段
所以可以用一个数据结构维护一段点的(1-p)的积
然后观察答案式子发现是可以线性做的
为了避免算重强制第二条边都在第一条边的左手系
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e3+5;
int n,m;
db ans,sum[N][10],pre[N],suf[N];
struct DOT{int x,y;db p;}a[N],p[N],rt;
ll cro(DOT x,DOT y,DOT rt) {
return (ll)(x.x-rt.x)*(y.y-rt.y)-(ll)(x.y-rt.y)*(y.x-rt.x);
}
bool cmp(DOT x,DOT y) {
return atan2(x.y-rt.y,x.x-rt.x)<atan2(y.y-rt.y,y.x-rt.x);
}
db get_sum(int l,int r) {
if (l>r) return 1;
db res=1;
fd(j,9,0)
if (l+(1<<j)<=r)
res*=sum[l][j],l+=(1<<j);
return res*sum[r][0];
}
int main() {
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d%d",&a[i].x,&a[i].y);
fo(i,1,m) scanf("%d%d%lf",&p[i].x,&p[i].y,&p[i].p);
fo(i,1,n) {
rt=a[i];
sort(p+1,p+m+1,cmp);
db now=0;ans+=1;
fo(j,1,m) sum[j][0]=1-p[j].p;
fo(k,1,9)
fo(j,1,m-(1<<(k-1)))
sum[j][k]=sum[j][k-1]*sum[j+(1<<(k-1))][k-1];
pre[0]=1;fo(j,1,m) pre[j]=pre[j-1]*(1-p[j].p);
suf[m+1]=1;fd(j,m,1) suf[j]=suf[j+1]*(1-p[j].p);
fo(j,1,m) ans-=pre[j-1]*suf[j+1]*p[j].p;ans-=pre[m];
int k=1;
while (k<m&&cro(p[k+1],p[1],rt)<0) {
++k;
now+=get_sum(k+1,m)*p[k].p;
}
ans-=now*p[1].p;if (k==1) k++;
fo(j,2,m) {
if (cro(p[j-1],p[j],rt)>=0) now-=get_sum(1,j-2)*get_sum(j+1,m)*p[j].p;
now*=(1-p[j-1].p);
while (k%m+1!=j&&cro(p[k%m+1],p[j],rt)<0) {
k=k%m+1;
if (j<k) now+=get_sum(1,j-1)*get_sum(k+1,m)*p[k].p;
else now+=get_sum(k+1,j-1)*p[k].p;
}
ans-=now*p[j].p;
if (j==k) k++;
}
}
printf("%.6lf\n",ans);
}