[BZOJ 1913][APIO 2011]信號覆蓋(計算幾何)


題目鏈接

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;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com