CodeForces 559 D.Randomizer(计算几何+期望)

256 篇文章 0 订阅
85 篇文章 0 订阅

Description

给出一个 n n 个点的凸包,等概率选则该凸包点集的大于等于三的子集形成一个新凸包,问该凸包内部整点的期望值

Input

第一行输入一整数n表示点数,之后 n n 行每行输入两个整数xi,yi表示第 i i 个点的坐标,按顺时针顺序给出这些点的坐标(3n105,109xi,yi109)

Output

输出子凸包内部整点的期望值,误差不超过 109 10 − 9

Sample Input

4
0 0
2 0
2 2
0 2

Sample Output

0.2

Solution

首先要用到皮克定理:顶点为整点的凸多边形,其面积 S S =内部整点数+0.5边界上整点数 1 − 1 ,故只要知道边界上点数和凸多边形面积即可得到凸多边形内部点数

问题求的是凸包内部整点数期望值,也即为原凸包内部整点数 不被凸包包含的整点数期望值,如果子凸包中两个相邻的顶点在原凸包中分别是第i个点和第 j j 个点,那么由i,i+1,...,j这些点组成的凸包内部的整点以及第 i i 个点与第j个点之间的连线上的整点(假设有 num n u m 个)都不被含有 (i,j) ( i , j ) 这条边的子凸包包含,假设 i i j之间有 k k 个点,那么剩下nk个点中要选出至少一个点与 i,j i , j 组成子凸包,方案数 2nk1 2 n − k − 1 ,而总方案数为 2n1nC2n 2 n − 1 − n − C n 2 ,那么 (i,j) ( i , j ) 这条边对不被凸包包含整点期望值的贡献为 num2nk12n1nC2n n u m ⋅ 2 n − k − 1 2 n − 1 − n − C n 2 ,枚举 i,j i , j ,维护由第 i,i+1,...,j i , i + 1 , . . . , j 个点组成凸包的面积以及边界上点的数量以求出 num n u m 即可,注意到答案对精度要求不太大,而 k k 比较大时概率很小可以忽略不计,故只需要枚举k35的情况即可,且当 n n 比较大的时候,可以把概率2nk12n1nCn2近似为 12k 1 2 k

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=100001;
struct node
{
    int x,y;
    node operator-(const node &b){return (node){x-b.x,y-b.y};}
    double operator*(const node &b){return 1.0*x*b.y-1.0*y*b.x;}
}a[maxn];
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
double pick(double S,double T)
{
    return S+1.0-0.5*T;
}
double f[105];
double Get(int n,int k)
{
    if(n>100)return 1.0/f[k+1];
    return (f[n-k-1]-1)/(f[n]-1-n-0.5*n*(n-1));
}
int main()
{
    f[0]=1;
    for(int i=1;i<=100;i++)f[i]=2.0*f[i-1];
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%d%d",&a[i].x,&a[i].y);
    double S=0,T=0;
    for(int i=0;i<n;i++)
    {
        int j=(i+1)%n;
        S+=(a[i]-a[0])*(a[j]-a[0])*0.5;
        T+=gcd(abs(a[j].x-a[i].x),abs(a[j].y-a[i].y));
    }
    double ans=0;
    for(int i=0;i<n;i++)
    {
        node pre=a[(i+1)%n]-a[i];
        double s=0,t=gcd(abs(pre.x),abs(pre.y));
        for(int j=(i+2)%n,k=2;k<=35&&(j+1)%n!=i;j=(j+1)%n,k++)
        {
            node now=a[j]-a[i];
            node temp=now-pre;
            double num=gcd(abs(now.x),abs(now.y));
            t+=num+gcd(abs(temp.x),abs(temp.y));
            s+=pre*now*0.5;
            ans+=(pick(s,t)+((j+1)%n==i?0:num-1))*Get(n,k);
            t-=num;
            pre=now;
        }
    }
    printf("%.10f\n",pick(S,T)-ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值