[BZOJ 1913][APIO 2011]信号覆盖(计算几何)

142 篇文章 0 订阅
31 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1913

思路

很容易发现,最终的答案为

C3n()

其实我们可以无视掉确定下圆的那三个点,那么答案可以表示为
ansC3n()+3

圆里面只会包含1个点,那么我们可以考虑枚举由确定圆的三个点和圆里面包含的那个点所构成的四边形。对于一个确定的四边形而言,每种方案要么是给ans贡献1种方案(凹四边形),要么是贡献2种方案(凸四边形),为什么呢?如下图
这里写图片描述
凹四边形相当于是一个三角形内包含1个点,此时只能找到一个圆,为 ans 贡献加1
而凸四边形的情况则有4种(图中只画了3种),由于题目限制了任意四点不共圆,因此其中只有2种是合法的,为 ans 贡献加2
因此最终答案可以表示为
num+2numC3n()+3

其实 num+num=C4n ,那么这两个集合中我们只要枚举其中一个集合的大小就可以了,显然凹四边形比凸四边形好枚举一些。答案简化为
num+2(C4nnum)C3n()+3

考虑如何求 num 。我们可以枚举那个被其他三个点构成的三角形覆盖的点 x ,然后求有多少个不同的三角形覆盖了这个点,就是凹四边形的个数了。这样做还是不好求,不如求有多少个不同的三角形没有覆盖此点。我们可以以点x为原点构建一个坐标系,将其他的点按照极角排序,这样就能得到一个极角按逆时针顺序的 n1 个点的序列。如下图
这里写图片描述
然后枚举点 i ,找在逆时针方向上在它后面的点p(注意 p i逆时针方向后面,这是为了防止重复计算),使得点 i+1 p 的极角线夹角刚好小于180度,且点 i+1 p+1 的极角线夹角刚好大于 180 度,这样的话,点 i+1 p 中选2个点,和点i构成的三角形显然是不覆盖点 x 的。我们可以维护一个指针代表点p,按极角序枚举点 i ,这样就能在线性时间内求出不覆盖点x的所有三角形个数。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 110000
#define PI 3.1415926535897384626

using namespace std;

typedef long long int LL;

int n;

struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y):x(_x),y(_y){}
}points[MAXN],tmp[MAXN];

int top=0;

Point operator-(Point a,Point b)
{
    return Point(a.x-b.x,a.y-b.y);
}

double cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

double ang[MAXN];

LL C(LL n,LL m)
{
    if(m==1) return n;
    if(m==2) return n*(n-1)/2;
    if(m==3) return n*(n-1)*(n-2)/6;
    return n*(n-1)*(n-2)*(n-3)/24;
}

LL ans=0; //ans=凹多边形个数

void calc(int x) //以点x为原点,找没有覆盖点x的三角形个数
{
    top=0; //!!!!!
    for(int i=1;i<=n;i++)
        if(i!=x)
            ang[++top]=atan2((points[i]-points[x]).y,(points[i]-points[x]).x);
    sort(ang+1,ang+top+1);
    LL tot=0; //tot=没有盖住点x的三角形个数
    int t=top;
    for(int i=1;i<=t;i++)
        ang[++top]=ang[i]+2*PI; //让负极角复制一遍变成正的
    int p=1; //x->i和x->p所夹的夹角小于等于2PI
    for(int i=1;i<=t;i++)
    {
        p=max(p,i+1);
        while(p<=top&&ang[p]<ang[i]+PI) p++;
        if(p-i-1>=2) tot+=C(p-i-1,2);
    }
    ans+=C(n-1,3)-tot;
}

int main()
{
    scanf("%d",&n);
    if(n<=3) { printf("0\n"); return 0; }
    for(int i=1;i<=n;i++)
        scanf("%lf%lf",&points[i].x,&points[i].y);
    for(int i=1;i<=n;i++)
        calc(i);
    LL ao=ans,tu=C(n,4)-ao; //ao=凹多边形个数,tu=凸多边形个数
    printf("%lf\n",(double)(ao+2*tu)/C(n,3)+3); //!!!!!
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值