【HNOI2016模拟4.4】Stage

本文介绍了一种使用计算几何的方法来解决概率问题。具体地,通过计算每个点不在观测范围内的概率,进而求解整个区域的概率。文章详细描述了算法思路,并提供了一段C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值