HDU 5784 How Many Triangles
给你一堆点,找锐角三角形。
TWO POINTER 思想。
统计出所有锐角和直=钝角的数目。
做法是这样的:对每个点对所有点极角排序,然后TWO POINTER计算每一个锐角(一个边上有好几个点也会被统计好几次),直角钝角。然后ans=(锐角个数-直角钝角个数*2)/3;因为每一个角度可能也只可能出现在一个三角形中。
但是一上来没弄懂,所以队友想了一个思路也能过,先统计直角和钝角的个数。然后C(n,3)统计所有情况,不能构成三角形的是三点共线的情况,所以n^2logn统计三点共线(TWO POINTER)。然后再减去直角钝角个数即可。两种方法均可过。
下面是我和队友死磕的方法。
#include <cstdio> #include <cstring> #include <vector> #include <cmath> #include <stack> #include <cstdlib> #include <queue> #include <map> #include <iostream> #include <algorithm> #include <bits/stdc++.h> using namespace std; typedef long long LL; struct Point { int x,y; Point(int _x = 0,int _y = 0) { x=_x; y=_y; } Point operator -(const Point &a) const { return Point(x-a.x,y-a.y); } } p[2010]; int n; LL xmul(Point a,Point b) { return (a.x*1ll*b.y-a.y*1ll*b.x); } LL dot(Point a,Point b) { return a.x*1ll*b.x+a.y*1ll*b.y; } bool cmp(Point a,Point b) { if (a.y*1ll*b.y<=0) { if (a.y>0||b.y>0) return a.y<b.y; if (a.y==0&&b.y==0)return a.x<b.x; } return xmul(a,b)>0; } vector<Point>vec; int main() { while (scanf ("%d",&n)!=EOF) { LL zhidun=0,rui=0,ans=0; for (int i=0; i<n; ++i) { scanf ("%d%d",&p[i].x,&p[i].y); } double temp=0; for (int cor=0; cor<n; ++cor) { vec.clear(); for (int i=0; i<n; ++i) { if (i!=cor) vec.push_back(p[i]-p[cor]); } sort(vec.begin(),vec.end(),cmp); for (int i=0; i<n-1; ++i) vec.push_back(vec[i]); int same=0,acute=0,eles=0; for (int i=0; i<n-1; ++i) { while (same<n-1&&xmul(vec[i],vec[same])==0&&dot(vec[i],vec[same])>0) ++same; acute=max(acute,same); while (acute<i+n-1&&xmul(vec[i],vec[acute])>0&&dot(vec[i],vec[acute])>0) ++acute; eles=max(eles,acute); while (eles<i+n-1&&xmul(vec[i],vec[eles])>0) ++eles; // rui+=acute-same; zhidun+=eles-acute; } // 下面计算以这个为核心,一条射线上三点共线的个数 // printf ("cor(%d,%d)\n",p[cor].x,p[cor].y); for (int j=0,k=0, i=0; i<n-1; i=j)//一条直线只遍历一次 { while (j<n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j; int x=j-i; if (i==0&&j==n-1)//一下到头,不用继续扫,直接跳出 { temp+=1.0*(x*(x-1))/2; break; } k=max(k,j); while (k<i+n-1&&xmul(vec[i],vec[k])>0) ++k;//第一次到达180位置 int fir=k; while (k<i+n-1&&xmul(vec[i],vec[k])==0) ++k;//刚刚超过180位置 int y=k-fir; x+=y; if (y==0)//反方向没有 { // printf ("2-----%f\n",1.0*(x*(x-1))/2); temp+=0.1*(x*(x-1))/2; } else//两端都有,后面的必然会被再统计到,这次就计算一半 { // printf ("4-----%f\n",1.0*(x*(x-1))/4); temp+=1.0*(x*(x-1))/4; } } // cout<<"temp="<<temp<<endl; } LL tt=(LL)(temp+0.5);//temp有精度问题 ans=n*(n-1)*1ll*(n-2)/6-tt/3-zhidun;//所有三个点减去三点共线情况就是三角形的情况,再减去直角钝角的就是答案 printf ("%I64d\n",ans); } return 0; }
下面是标解的方法:
#include <cstdio> #include <cstring> #include <vector> #include <cmath> #include <stack> #include <cstdlib> #include <queue> #include <map> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; struct Point { int x,y; Point(int _x = 0,int _y = 0) { x=_x; y=_y; } Point operator -(const Point &a) const { return Point(x-a.x,y-a.y); } }p[2010]; int n; LL xmul(const Point &a,const Point &b) { return a.x * 1ll * b.y - a.y * 1ll * b.x; } LL dot(const Point &a,const Point &b) { return a.x * 1ll * b.x + a.y * 1ll * b.y; } bool cmp(const Point &a,const Point &b) { if (a.y * 1ll * b.y <= 0) { if (a.y > 0 || b.y > 0) return a.y < b.y; if (a.y == 0 && b.y == 0) return a.x < b.x; } return xmul(a,b) > 0; } vector<Point>vec; int main() { while (scanf ("%d",&n)!=EOF) { LL zhidun=0,rui=0,ans=0; for (int i=0;i<n;++i) { scanf ("%d%d",&p[i].x,&p[i].y); } for (int cor=0;cor<n;++cor) { vec.clear(); for (int i=0;i<n;++i) { if (i!=cor) vec.emplace_back(p[i]-p[cor]); } sort(vec.begin(),vec.end(),cmp); vec.insert(vec.end(),vec.begin(),vec.end()); int j=0,k=0,r=0; for (int i=0;i<n-1;++i) { while (j<i+n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j; k=max(k,j); while (k<i+n-1&&xmul(vec[i],vec[k])>0&&dot(vec[i],vec[k])>0) ++k; r=max(r,k); while (r<i+n-1&&xmul(vec[i],vec[r])>0) ++r; rui+=k-j; zhidun+=r-k; } } ans=(rui-2*zhidun)/3; printf ("%I64d\n",ans); } return 0; }
于是又多了一个n^2logn判断三点共线个数的版子,首发,在网上还没看到
#include <cstdio> #include <cstring> #include <vector> #include <cmath> #include <stack> #include <cstdlib> #include <queue> #include <map> #include <iostream> #include <algorithm> #include <bits/stdc++.h> using namespace std; typedef long long LL; struct Point { int x,y; Point(int _x = 0,int _y = 0) { x=_x; y=_y; } Point operator -(const Point &a) const { return Point(x-a.x,y-a.y); } } p[2010]; int n; LL xmul(Point a,Point b) { return (a.x*1ll*b.y-a.y*1ll*b.x); } LL dot(Point a,Point b) { return a.x*1ll*b.x+a.y*1ll*b.y; } bool cmp(Point a,Point b) { if (a.y*1ll*b.y<=0) { if (a.y>0||b.y>0) return a.y<b.y; if (a.y==0&&b.y==0)return a.x<b.x; } return xmul(a,b)>0; } vector<Point>vec; int main() { while (scanf ("%d",&n)!=EOF) { for (int i=0; i<n; ++i) { scanf ("%d%d",&p[i].x,&p[i].y); } double temp=0; for (int cor=0; cor<n; ++cor) { vec.clear(); for (int i=0; i<n; ++i) { if (i!=cor) vec.push_back(p[i]-p[cor]); } sort(vec.begin(),vec.end(),cmp); for (int i=0; i<n-1; ++i) vec.push_back(vec[i]); // 下面计算以这个为核心,一条射线上三点共线的个数 // printf ("cor(%d,%d)\n",p[cor].x,p[cor].y); for (int j=0,k=0, i=0; i<n-1; i=j)//一条直线只遍历一次 { while (j<n-1&&xmul(vec[i],vec[j])==0&&dot(vec[i],vec[j])>0) ++j; int x=j-i; if (i==0&&j==n-1)//一下到头,不用继续扫,直接跳出 { temp+=1.0*(x*(x-1))/2; break; } k=max(k,j); while (k<i+n-1&&xmul(vec[i],vec[k])>0) ++k;//第一次到达180位置 int fir=k; while (k<i+n-1&&xmul(vec[i],vec[k])==0) ++k;//刚刚超过180位置 int y=k-fir; x+=y; if (y==0)//反方向没有 { // printf ("2-----%f\n",1.0*(x*(x-1))/2); temp+=0.1*(x*(x-1))/2; } else//两端都有,后面的必然会被再统计到,这次就计算一半 { // printf ("4-----%f\n",1.0*(x*(x-1))/4); temp+=1.0*(x*(x-1))/4; } } // cout<<"temp="<<temp<<endl; } LL tt=(LL)(temp+0.5);//temp有精度问题 printf ("%I64d\n",tt/3); } return 0; }