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

来源:互联网 发布:ubuntu xorg 安装 编辑:程序博客网 时间:2024/06/05 01:53

题目

http://cogs.pro/cogs/problem/problem.php?pid=543

分析

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

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

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



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

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



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

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



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

这样问题就好办了:先把所有点按照极角排序,然后线性扫描下去,用两个指针维护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;}


0 0
原创粉丝点击