【BZOJ】1914: [Usaco2010 OPen]Triangle Counting 数三角形
【题意】给定坐标系上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; }
注意叉积计算后作为int,不是double。