【BZOJ-1913】signaling信號覆蓋 極角排序 + 組合

1913: [Apio2010]signaling 信號覆蓋

Time Limit: 20 Sec  Memory Limit: 64 MB
Submit: 1232  Solved: 506
[Submit][Status][Discuss]

Description

Input

輸入第一行包含一個正整數 n, 表示房子的總數。接下來有 n 行,分別表示 每個房子的位置。對於 i = 1, 2, .., n, 第i 個房子的座標用一對整數 xi和yi來表 示,中間用空格隔開。

Output

輸出文件包含一個實數,表示平均有多少個房子被信號所覆蓋,需保證輸出 結果與精確值的絕對偏差不超過0.01。

Sample Input

4
0 2
4 4
0 0
2 0

Sample Output

3.500

HINT

3.5, 3.50, 3.500, … 中的任何一個輸出均爲正確。此外,3.49, 3.51, 3.499999,…等也都是可被接受的輸出。 
【數據範圍】 
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。 php

Source

Solution

這題的思路仍是很巧妙的QwQios

首先要是枚舉點,複雜度是$O(N^4)$的,並且難以進一步優化。優化

這裏保證四點不共圓,因此能夠考慮一下從多邊形對答案的貢獻的入手。spa

對於一個凸多邊形,它對答案的貢獻是$2$,即以一組對角和$>\pi$的兩個點中的任意一個和另外兩個點作圓,必定能覆蓋另外一個點。blog

對於一個凹多邊形,它對答案的貢獻只有$1$,即以靠外三個點作圓能夠覆蓋凹進去的那個點。排序

因此能夠獲得答案$ans=3+\frac {2*cnt_{凸}+1*cnt_{凹}} {C^{N}_{3}}$ip

而後就是如何快速的求出凸多邊形和凹多邊形的數量了。get

由於這裏保證了不存在三點共線,考慮$O(N^2)$的枚舉兩個點組成的一條直線,算出直線兩邊的兩個點和直線上兩點組成多邊形的狀況。string

對於直線兩邊的點數量分別是$a$和$b$,就對答案加入$C^{a}_{2}$和$C^{b}_{2}$,這裏利用極角排序,能夠利用單調性作到直線旋轉時的均攤$O(1)$的複雜度。it

而後考慮這樣統計出來的是什麼,對於一個凸多邊形,利用這樣的方式,會被統計四次,對於一個凹多邊形,這樣會統計三次,因此這樣的答案就是$4*cnt_{凸}+3*cnt_{凹}$

而後減掉$2*(cnt_{凸}+cnt_{凹})=2*cnt_{總}=2*C^{n}_{4}$,就能夠獲得$2*cnt_{凸}+1*cnt_{凹}$。同時就能夠獲得答案。

因此總複雜度是$O(N^2)$就獲得解決。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define Pi acos(-1.0)
#define MAXN 2010
 
int N,tot;
struct Point{
    int x,y;
}P[MAXN];
double k[MAXN<<1],ans;
 
inline double C(int n,int m)
{
    double re=1;
    for (int i=n-m+1; i<=n; i++) re=re*i;
    for (int i=1; i<=m; i++) re=re/i;
    return re;
}
 
int main()
{
    N=read();
    for (int i=1; i<=N; i++) P[i].x=read(),P[i].y=read();
     
    for (int i=1; i<=N; i++) {
        tot=0;
        for (int j=1; j<=N; j++)
            if (i!=j) k[++tot]=atan2(P[j].x-P[i].x,P[j].y-P[i].y);
        sort(k+1,k+tot+1);
        for (int j=1; j<=tot; j++) k[tot+j]=k[j]+2.0*Pi;
        for (int j=1,now=1; j<=tot; j++) {
            while (k[now]-k[j]<Pi) now++;
            int cnt=now-j-1;
            ans+=C(cnt,2);
        }
    }
     
    ans-=C(N,4)*2;
     
    printf("%.6lf\n",ans/C(N,3)+3.0);
    return 0;
}
相關文章
相關標籤/搜索