BZOJ 1914: [Usaco2010 OPen]Triangle Counting 数三角形
http://www.lydsy.com/JudgeOnline/problem.php?id=1914
题意:给定平面上N(<=10^5)个整点,任意三点可以组成一个三角形,求把原点包含在内部的三角形个数。保证原点不在两点连线上。
我们反过来考虑,用总的三角形个数减去非法的三角形个数。
总的三角形个数是
然后考虑非法三角形的情况,原点在三角形外部。作原点和三个点的连线,我们会发现一定是形成一个锐角,所以答案只跟每个点的极角有关。
于是把每个点按照极角序排序,用单调队列维护一个半平面上的点,每次加入一个点时,删除队尾与该点成顺时针的点,此时对答案的贡献就是
一些细节:把点按x坐标正负分成左右两部分,排序的时候就可以用叉积,单调队列扫的时候也用叉积判断。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
struct P{ll x, y;}A[MAXN], B[MAXN], q[MAXN<<2];
ll operator*(P a, P b){return a.x*b.y-a.y*b.x;} //叉积
int cmp(P a, P b){return a*b<0;}
int na, nb, N;
int main()
{
scanf("%d", &N);
for(int i=0; i<N; ++i){
P p; scanf("%lld%lld", &p.x, &p.y);
if(p.x>0 || (p.x==0 && p.y>0)) A[na++]=p; //右半部分
else B[nb++]=p; //左半部分
}
sort(A, A+na, cmp); sort(B, B+nb, cmp);
int hd=0, tl=0;
for(int i=0; i<nb; ++i) q[tl++]=B[i];
ll ans = (ll)N*(N-1)*(N-2)/6;
for(int i=0; i<na; ++i){
while(hd<tl && A[i]*q[hd]<0) hd++;
ans -= (ll)(tl-hd)*(tl-hd-1)/2;
q[tl++]=A[i];
}
for(int i=0; i<nb; ++i){
while(hd<tl && B[i]*q[hd]<0) hd++;
ans -= (ll)(tl-hd)*(tl-hd-1)/2;
q[tl++]=B[i];
}
printf("%lld\n", ans);
return 0;
}
退役