Problem How Many Triangles (HDU 5784)
题目大意
给定平面上的n个点(n《2000),询问可以组成多少个锐角三角形。
解题分析
直接统计锐角三角形较困难,考虑问题的反面,统计直角三角形、钝角三角形、平角三角形(暂时这么叫吧QAQ)。
首先枚举三角形的一个端点A,对其他点进行象限为第一关键字,极角为第二关键字排序。
然后使用三个指针,进行O(n)的扫描。
具体做法为用 i 指针指向三角形的第二个端点B。我们可以假想通过平移和旋转,把A点置于平面直角坐标系的原点,把B点置于x轴的正方向。那么可以与AB组成钝角或直角的点就在三四象限或者y轴。
将 j 指针指向第一象限内可以组成锐角的最靠后的点,将k指针从j + 1 开始扫描至最后一个可以组成钝角的点,然后统计对答案的贡献。
之后将 i 指针 +1,继续扫描。
注意一些特殊的情况,可以写个暴力对拍,造点小数据debug。
参考程序
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 #define eps 1e-8 7 8 struct P{ 9 long long x,y; 10 int f; 11 friend P operator -(P a,P b){ 12 return (P){a.x-b.x,a.y-b.y}; 13 } 14 }a[20008],b[20008],S; 15 16 int n,m; 17 18 inline long long cross(P a,P b,P c){ //ab X ac 19 return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); 20 } 21 22 inline int calc(P a){ 23 if (a.x>0 && a.y==0) return 1; 24 if (a.x>0 && a.y>0) return 1; 25 if (a.x==0 && a.y>0) return 2; 26 if (a.x<0 && a.y>0) return 2; 27 if (a.x<0 && a.y==0) return 3; 28 if (a.x<0 && a.y<0) return 3; 29 if (a.x==0 && a.y<0) return 4; 30 if (a.x>0 && a.y<0) return 4; 31 } 32 33 inline bool cmp(const P a,const P b) { 34 if (a.f<b.f) return true; 35 if (a.f>b.f) return false; 36 long long tmp=cross(S,a,b); 37 if (tmp>0) return true; 38 return false; 39 } 40 41 inline bool ok(P a,P b,P c){ 42 long long tmp=(b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y); 43 if (tmp>0) return true; 44 return false; 45 } 46 47 int main(){ 48 while (~scanf("%d",&n)){ 49 for (int i=1;i<=n;i++) scanf("%I64d %I64d",&a[i].x,&a[i].y); 50 long long sum=0; 51 for (int ii=1;ii<=n;ii++){ 52 S=a[ii]; m=0; 53 for (int j=1;j<=n;j++) 54 if (j!=ii) b[++m]=a[j]; 55 for (int i=1;i<=m;i++) b[i].f=calc(b[i]-S); 56 sort(b+1,b+m+1,cmp); 57 int i=1,j=1,k=1; 58 while (ok(S,b[i],b[j]) && cross(S,b[i],b[j])>=0 && j<=m) j++; 59 if (j==m+1) continue; 60 j--; k=j+1; 61 while (i<=m) 62 { 63 if (!ok(S,b[i],b[k])){ 64 while (!ok(S,b[i],b[k+1]) && k<m) k++; 65 sum+=k-j; 66 } 67 i++; 68 if (j<i) j=i; 69 while (ok(S,b[i],b[j+1]) && cross(S,b[i],b[j+1])>0 && j<m) j++; 70 if (k<=j) k=j+1; 71 if (k>m) break; 72 } 73 } 74 long long p=1ll*n*(n-1)*(n-2)/6; 75 printf("%I64d\n",p-sum); 76 } 77 }