洛谷 P3630 [APIO2010]信号覆盖

190 篇文章 2 订阅
48 篇文章 0 订阅

题面

题意

给出n个点(没有三点共线),取三个点做它们的外接圆,问所有取法中覆盖的点的平均个数是多少.

方法

考虑每一个四边形对答案的贡献值(除了取的三个点外的覆盖点),每个凹四边形的贡献值为1,因为只有取最外面的三点才能多覆盖一个,而凸四边形的贡献值为2,因为有一对对角之和小于180,另一对对角大于180.
之后问题就被转化为了求所有组成的四边形的凹四边形的个数.

求凹四边形的个数可以从凹点进行考虑,枚举凹点,发现若有三点不能同时处于一条过凹点的直线一侧,则这四个点可以构成一个凹四边形.
因此可以先算出能够同时处于直线一边的点的种类数,再推出该凹点所代表的凹四边形数,计算方法:
确定此时枚举的凹点后,先根据角的度数排序,排序方法可以根据如下定义法:
以此时凹点为原点,建立平面直角坐标系,根据此时点与x轴正半轴的角度来排序,定义方法:
1.第一象限角,定义为其正弦值
2.第二象限角,定义为其余弦值的绝对值+1
3.第三象限角,定义为其正弦值的绝对值+2
4.第四象限角,定义为其余弦值+3.
这种方法可以满足角随其大小的改变而改变,相差180度的角的差为2.
据此算出各个角的度数并排序,一个区间一个区间进行累加,最后统计出贡献之和处于圆的个数+3即为答案.

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define db double
#define ll long long
#define N 1520
using namespace std;

ll n,aa,now;
db ans,an,tmp,sum,a[N*2];
struct Node
{
    db x,y;
} node[N];

inline db len(Node u,Node v)
{
    return sqrt((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y));
}

inline db qj(Node u,Node v)
{
    if(u.x<v.x)
    {
        if(u.y<v.y)
        {
            return fabs(u.y-v.y)/len(u,v);
        }
        if(u.y==v.y) return 4;
        return 3+fabs(u.x-v.x)/len(u,v);
    }
    if(u.x==v.x)
    {
        if(u.y<v.y) return 1;
        return 3;
    }
    if(u.y==v.y) return 2;
    if(u.y<v.y)
    {
        return 1+fabs(u.x-v.x)/len(u,v);
    }
    return 2+fabs(u.y-v.y)/len(u,v);
}

int main()
{
    int i,j,k;
    cin>>n;
    sum=n*(n-1)*(n-2)*(n-3)/24;
    for(i=1; i<=n; i++)
    {
        scanf("%lf%lf",&node[i].x,&node[i].y);
    }
    for(i=1; i<=n; i++)
    {
        an=aa=0,now=1;
        a[0]=0;
        for(j=1; j<=n; j++)
        {
            if(j==i) continue;
            aa++;
            a[aa]=qj(node[i],node[j]);
        }
        sort(a+1,a+aa+1);
        for(;aa<2*n-2;)
        {
            aa++;
            a[aa]=a[aa-n+1];
        }

        for(j=1;a[j+1]<2&&j<n-1;j++);
        for(;now<=n-1;now++)
        {
            for(;a[j+1]-a[now]+4*(j+1>n-1)<2;j++);
            an+=(j-now)*(j-now-1)/2;
        }
        ans+=(n-1)*(n-2)*(n-3)/6-an;
    }
    sum=n*(n-1)/2*(n-2)/3*(n-3)/4;
    printf("%lf",((sum-ans)*2+ans)/(n*(n-1)*(n-2)/6)+3);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值