[USACO Open10]数三角形Triangle Counting解题报告

题目

http://cojs.tk/cogs/problem/problem.php?pid=543

分析

这道题的要点在于:含原点的三角形不好数,我们数不含原点的三角形,最后用C(N,3)扣掉它就是答案了。

怎么数“不含原点的三角形”呢?

画出我们所在的坐标系。我们拿一条过原点的直线l,按极角序去扫描:



直线l分成两条射线,一个是a,另一个是b。它绕着原点(Bessie所在点)旋转,请自行想象在孙悟空手中旋转的金箍棒。
(猴哥,猴哥,你真了不得……)

然后关键在于:每当射线a扫到一个点P时,我们统计P和直线l“左侧”所有点形成的三角形(严格地讲,是和a叉积为正的半平面):

[USACO Open10]数三角形Triangle Counting解题报告_第1张图片

像这样,图中展示了a扫到p点时统计的三个三角形。

每一个不包含原点的三角形——无论形状多么畸形,位置多么奇特,总会被计入统计一次且只有一次。下面展示了几个三角形,和它们被计入统计时,扫描线a的位置:

[USACO Open10]数三角形Triangle Counting解题报告_第2张图片

(注意,这个点不一定是极角最小的点)

这样问题就好办了:先把所有点按照极角排序,然后线性扫描下去,用两个指针维护a和b的位置,在扫描的过程中,减掉所有应予统计的三角形。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int SIZEN=200010;
class Point{
public:
	LL x,y;
	double ang;
};
bool cmp(const Point &a,const Point &b){
	return a.ang<b.ang;
}
LL cross(const Point &a,const Point &b){
	return a.x*b.y-b.x*a.y;
}
int N;
Point P[SIZEN];
void work(void){
	sort(P+1,P+1+N,cmp);
	for(int i=1;i<=N;i++) P[N+i]=P[i];
	LL ans=(LL)N*(N-1)*(N-2)/6;
	for(int i=1,j=1;i<=N;i++){
		while(j<i+N&&cross(P[i],P[j])>=0) j++;
		LL k=j-i-1;
		ans-=k*(k-1)/2;
	}
	printf("%lld\n",ans);
}
void read(void){
	scanf("%d",&N);
	for(int i=1;i<=N;i++){
		scanf("%lld%lld",&P[i].x,&P[i].y);
		P[i].ang=atan2(P[i].y+0.0,P[i].x+0.0);
	}
}
int main(){
	freopen("tricount.in","r",stdin);
	freopen("tricount.out","w",stdout);
	read();
	work();
	return 0;
}


你可能感兴趣的:(数学,USACO,解题报告,计算几何)