APIO2010 信号覆盖

APIO2010 信号覆盖

【问题描述】
  一家电信公司正在北京城搭建一个 GSM 网络。城市里共有 n 个房子需要被信号覆盖。由于经费的限制,电信公司只能安装一个天线。
  这里将每个房子用一个点坐标来表示。为了简化天线的放置,电信公司将会选择其中的3个房子作一个外接圆,然后将天线放在圆的中心,所有位于这个圆内或者圆的边界上的房子都将被天线的信号所覆盖。
  电信公司将会随机选择城市中的3个房子来搭建天线,他们想知道在所有可能放置天线的方案中平均会有多少个房子被信号覆盖。
  例如,假设共有 4个房子A, B, C, D,它们的位置如下图:

 

如果我们选择ABC或者BCD三个点搭建的外接圆,所有的房子都会被覆盖。如果我们选择ACD 或者ABD,剩下的房子将不会在天线的信号覆盖范围内。因此平均有(4 + 4 + 3 + 3) / 4 = 3.50 个房子被信号覆盖。
  给定所有房子的位置,你的任务是计算平均有多少个房子被信号覆盖。假定每一个房子都有一个二维的整数坐标,并且保证任何三个房子都不在同一条直线上,任何四个房子都不在同一个圆上。

【输入文件】

    输入第一行包含一个正整数 n, 表示房子的总数。接下来有 n 行,分别表示每一个房子的位置。对于 i = 1, 2, .., n, 第i 个房子的坐标用一对整数 xi和yi来表示,中间用空格隔开。 

【输出文件】

   输出文件包含一个实数,表示平均有多少个房子被信号所覆盖,保留两位小数

【输入样例】

 4 

0 2 

4 4 

0 0 

2 0 

 

【输出样例】

3.50

【数据范围】
100%的数据保证,对于 i  = 1, 2, ..,  n, 第 i 个房子的坐标(xi,  yi)为整数且 –1,000,000 ≤ xi, yi ≤ 1,000,000. 任何三个房子不在同一条直线上,任何四个房子不在同一个圆上;
40%的数据,n ≤ 100;
70%的数据,n ≤ 500;
100%的数据,3 ≤ n ≤ 1,500。
【题目分析】

N^4的算法很好想,枚举三个点组成一个圆,再枚举一个点判断点是否在圆内,统计输出就可以了,但这样显然只能拿到40分。

对于任意4个点,组成的图形只有两种,一种是凸四边形,一种是凹四边形,即一个点在另外三个点组成的三角形内。

对于凸四边形,必有一组对角大于180°,此时这组对角所对应的顶点必然在另外三个点组成的圆中,所以每个凸四边形对于总的答案贡献两个点。

对于凹四边形,设D在三角形ABC内,那么只有D在三角形ABC组成的圆中,A,B和C都不在另外三个点的圆中,所以凹四边形对总的答案贡献一个点。

由于总的四边形个数一定,我们只需要求出凹四边形的个数。

首先枚举凹点D,以D作为极点,x轴正半轴为极轴进行极角排序,对于一个合法的三角形ABC,过D的任意一条直线都不能使三个点在D的同一侧,这样我们就用总的三角形个数C(n-1,3)来减去同一侧的三角形个数。这样枚举一个点A,找到A到C的角小于180°的最远点C,设A的编号为i,C的编号为j,那么此时在一侧的三角形个数为C(j-i,2),由于C对于每一个A是单调的,所以每一次的复杂度均为n,总的复杂度就是n^2,加上排序的复杂度,总复杂度为n^2logn

【代码实现】

Code
 1 program test3;
 2 uses math;
 3 var x,y:array[0..1500]of extended;
 4     p,q:array[0..3000]of extended;
 5     i,j,m,k:longint;
 6     tot,sum1,sum2,sum,now,n:int64;
 7     ans:extended;
 8 procedure kp(l,r:longint);
 9 var i,j:longint;
10     x,y:extended;
11 begin
12     i:=l;j:=r;
13     x:=p[(l+r)shr 1];
14     repeat
15         while p[i]<x do inc(i);
16         while p[j]>x do dec(j);
17         if i<=j then
18         begin
19             y:=p[i];
20             p[i]:=p[j];
21             p[j]:=y;
22             inc(i);
23             dec(j);
24         end;
25     until i>j;
26     if l<j then kp(l,j);
27     if i<r then kp(i,r);
28 end;
29 begin
30     readln(n);
31     for i:=1 to n do readln(x[i],y[i]);
32     tot:=n*(n-1)*(n-2)div 6;
33     for k:=1 to n do
34     begin
35         p[k]:=0;
36         for i:=1 to n do
37         begin
38             if i=k then continue;
39             q[i]:=(x[i]-x[k])/sqrt(sqr(x[i]-x[k])+sqr(y[i]-y[k]));
40             p[i]:=arccos(q[i]);
41             if y[i]<y[k] then p[i]:=2*pi-p[i];
42         end;
43         kp(1,n);
44         for i:=2 to n do p[i+n-1]:=p[i];
45         j:=2;
46         now:=0;
47         for i:=2 to n do
48         begin
49             while (((p[j+1]-p[i]<=pi)and(p[j+1]>=p[i]))or(p[j+1]-p[i]<-pi))and(j<i+n-2) do inc(j);
50             now:=now+(j-i)*(j-i-1)div 2;
51         end;
52         sum:=sum+(n-1)*(n-2)*(n-3)div 6-now;
53     end;
54     sum1:=sum;
55     sum2:=n*(n-1)*(n-2)*(n-3)div 24-sum1;
56     sum:=sum1+sum2*2+tot*3;
57     ans:=sum/tot;
58     writeln(ans:0:6);
59 end.


 

转载于:https://www.cnblogs.com/whitecloth/archive/2012/05/05/2484879.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值