题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=115760#problem/A
题目意思大致为由n个点(n小于100)和m个金矿(m小于1000),对于任意n点中每三个点都不会共线,对于n个点中每三个点构成的三角形中,金矿落在其中的个数为奇数时,这是符合题意的三角形,求共有多少个三角形满足题意。
据说暴力枚举所有三角形然后枚举每个点是不是在其中(复杂度10^9)是可以过的,但不是正解。
下面给出幻神的精彩思路:对于每两个点(复杂度n^2),枚举每一个点是不是在他们的下面,这个预处理的复杂度是10^7,得到每条边的下面的点的个数。然后枚举每三个三角形,对于这个三角形的,比方说,i,j,k按照它们的x坐标升序排列,且j点在edge(i,k)上,那么只要edge(i,j)边下的点的数目加上edge(j,k)边下的数目减去edge(i,k)边下的数目就是在这个三角形内的点的个数;另外如果j点在edge(i,k)下,那么只要反过来相减就可以了。综上,只要升序以后,取绝对值即可。另外的一个技巧是,只要给n个点按照x坐标的大小排序一下,代码写起来就会非常方便。但是这里有个坑点,因为边的权值是10的5次方级别的,所以在使用叉积判断点是否在边下的时候可能会爆int!所以做一题绝对不能大意啊- 。-
AC代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #include <math.h> 5 using namespace std; 6 typedef long long ll; 7 8 int cnt[105][105]; 9 struct point 10 { 11 int x,y; 12 bool operator < (const point & A)const 13 { 14 return x<A.x; 15 } 16 }p[1300]; 17 18 ll cross(point a,point b,point c) 19 { 20 ll x1=(ll)b.x-a.x; 21 ll y1=(ll)b.y-a.y; 22 ll x2=(ll)c.x-a.x; 23 ll y2=(ll)c.y-a.y; 24 return x1*y2-y1*x2; 25 } 26 int main() 27 { 28 int n,m; 29 int kase=0; 30 while(scanf("%d%d",&n,&m)==2) 31 { 32 memset(cnt,0,sizeof(cnt)); 33 for(int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y); 34 for(int i=1;i<=m;i++) scanf("%d%d",&p[i+n].x,&p[i+n].y); 35 36 sort(p+1,p+1+n); 37 for(int i=1;i<=n-1;i++) 38 for(int j=i+1;j<=n;j++) 39 for(int k=n+1;k<=n+m;k++) 40 { 41 if(p[j].x>p[k].x&&p[k].x>p[i].x && cross(p[i],p[j],p[k])<0) cnt[i][j]++; 42 } 43 44 int ans=0; 45 for(int i=1;i<=n-2;i++) 46 for(int j=i+1;j<=n-1;j++) 47 for(int k=j+1;k<=n;k++) 48 { 49 int t=abs(cnt[i][j]+cnt[j][k]-cnt[i][k]); 50 if(t&1) ans++; 51 } 52 printf("Case %d: %d ",++kase,ans); 53 } 54 return 0; 55 }
另外有仓鼠学长的思路是,枚举三边,上面的两边的下面和下面一边的上面存在的点取个交集就是这个三角形内部的点的个数,,觉得不如幻神的思路来得好,而且觉得写起来也麻烦,不过作为一种思路记下来总是好的- 。-