1. 程式人生 > >【BZOJ】1914: [Usaco2010 OPen]Triangle Counting 數三角形

【BZOJ】1914: [Usaco2010 OPen]Triangle Counting 數三角形

long blog fabs tdi int ria acf scanf ons

【題意】給定坐標系上n個點,求能構成的包含原點的三角形個數,n<=10^5。

【算法】極角排序

【題解】補集思想,三角形個數為C(n,3)-不含原點三角形。

將所有點極角排序。

對於一個點和原點構成的直線,如果選擇這個點和直線一側的兩個點就可以構成不含原點的三角形。

每個點只統計半圈,這樣掃1~n下來每個點就會被統計若幹次和統計若幹次,加起來剛好是答案。這也是基環樹DP中的慣用套路。

這樣只要用雙指針找到半圈內有多少點即可,比較一個點是否在直線一側可以比較直線向量和目標點向量的叉積是否>0。

技術分享
#include<cstdio>
#include<cstring>
#include
<algorithm> #include<cmath> #define ll long long using namespace std; const int maxn=100010,eps=1e-8; int n; long long ans; struct point{ ll x,y;double angle; ll operator *(const point a)const{ return x*a.y-a.x*y;// } }a[maxn]; bool cmp(point a,point b){return fabs(a.angle-b.angle)<eps?a.x<b.x:a.angle<b.angle;}
int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%lld%lld",&a[i].x,&a[i].y); a[i].angle=atan2(a[i].y,a[i].x); } sort(a,a+n,cmp); int r=1,num=0;ans=0; for(int i=0;i<n;i++){ while(r!=i&&a[i]*a[r]>=0)r=(r+1)%n,num++; ans
+=1ll*num*(num-1)/2; num--; } printf("%lld",1ll*n*(n-1)*(n-2)/6-ans); return 0; }
View Code

註意叉積計算後作為int,不是double。

【BZOJ】1914: [Usaco2010 OPen]Triangle Counting 數三角形