【计算几何+极角排序+爆ll】E. Convex
https://www.bnuoj.com/v3/contest_show.php?cid=9147#problem/E
【题意】
给定n个点的坐标,可以选择其中的四个点构造凸四边形,问最多能构造多少个凸四边形?
【思路】
凸四边形的个数等于C(n,4)-凹四边形的个数。
凹四边形的特点是有一个顶点被另外三个顶点围成的三角形包了起来。
所以现在的问题就是找凹四边形。
我们可以枚举每个点,作为被三角形包围的中心点o。怎么找这样包围中心点的三角形?
这样的三角形一定是在存在一条经过中心点的直线,三角形的三个顶点在直线的同一侧。
那么枚举三角形的一个顶点x,另两个顶点一定在o和x的连线ox的上半平面内。而且这样做类似与尺取,只需O(n)的复杂度。
最后注意的一点是:
printf("%I64d\n",-3LL*n*(n-1)*(n-2)*(n-3)/24+cnt);
printf("%I64d\n",n*(n-1)*(n-2)*(n-3)/24LL-(n*(n-1)*(n-2)*(n-3)/6LL-cnt));
的区别。
前者在前面成了3LL,所以计算连乘的时候是把int转化为ll,不会爆
后者n*(n-1)*(n-2)*(n-3)在计算的过程中已经爆了。
解决办法有两种:
在前面乘以1LL;n变成ll
【Accelerate】
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<algorithm> 7 8 using namespace std; 9 typedef long long ll; 10 const int maxn=710; 11 int n; 12 ll xx[maxn]; 13 ll yy[maxn]; 14 int cur; 15 double dis(ll x1,ll y1,ll x2,ll y2){return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));} 16 struct Point 17 { 18 ll x; 19 ll y; 20 double dis; 21 double alf; 22 Point(){} 23 Point(ll _x,ll _y):x(_x),y(_y){} 24 Point operator -(const Point &t) const 25 { 26 return Point(x-t.x,y-t.y); 27 } 28 ll operator ^(const Point &t)const 29 { 30 return (x*t.y)-(y*t.x); 31 } 32 double alfa() 33 { 34 if(y>yy[cur])return acos((x-xx[cur])/dis); 35 return -acos((x-xx[cur])/dis); 36 } 37 }p[maxn]; 38 39 bool cmp(Point a,Point b) 40 { 41 if(b.x==xx[cur]&&b.y==yy[cur]) 42 { 43 return true; 44 } 45 if(a.x==xx[cur]&&a.y==yy[cur]) 46 { 47 return false; 48 } 49 return a.alf<b.alf; 50 } 51 52 int main() 53 { 54 int T; 55 scanf("%d",&T); 56 while(T--) 57 { 58 scanf("%d",&n); 59 for(int i=0;i<n;i++) 60 { 61 cin>>xx[i]>>yy[i]; 62 p[i]=Point(xx[i],yy[i]); 63 } 64 ll cnt=0; 65 for(cur=0;cur<n;cur++) 66 { 67 68 int l=1; 69 Point o(xx[cur],yy[cur]); 70 for(int i=0;i<n;i++){p[i].dis=dis(p[i].x,p[i].y,xx[cur],yy[cur]);p[i].alf=p[i].alfa();} 71 sort(p,p+n,cmp); 72 for(int i=0;i<n-1;i++) 73 { 74 while(((p[i]-o)^(p[l]-o))>0) 75 { 76 l=(l+1)%(n-1); 77 } 78 int len=(l-i-1+n-1)%(n-1); 79 cnt+=len*(len-1)/2; 80 } 81 } 82 // ll ans=n*(n-1)*(n-2)*(n-3)/24LL-(n*(n-1)*(n-2)*(n-3)/6LL-cnt);//注意,这样会爆 83 // printf("%I64d\n",-3LL*n*(n-1)*(n-2)*(n-3)/24+cnt);//前面乘以3LL,不会爆 84 printf("%I64d\n",1LL*n*(n-1)*(n-2)*(n-3)/24LL-(1LL*n*(n-1)*(n-2)*(n-3)/6LL-cnt)); 85 } 86 return 0; 87 }
【知识点】
判断是不是在一个半平面内用到了叉积的性质:
叉积的一个非常重要的性质是通过它的符号判断两向量相互之间的顺逆时针关系:设向量P=(x1,y1),Q=(x2,y2)
如果P*Q>0则P在Q的顺时针方向;
如果P*Q=0则P与Q共线,可能同向,与可能反向;
如果P*Q<0则P在Q的逆时针方向。