1. 程式人生 > >【計算幾何+極角排序+爆ll】E. Convex

【計算幾何+極角排序+爆ll】E. Convex

lfa string ret == mes 過程 一個 區別 problem

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 }
View Code

【知識點】

判斷是不是在一個半平面內用到了叉積的性質:

叉積的一個非常重要的性質是通過它的符號判斷兩向量相互之間的順逆時針關系:設向量P=(x1,y1),Q=(x2,y2)
如果P*Q>0則P在Q的順時針方向;
如果P*Q=0則P與Q共線,可能同向,與可能反向;
如果P*Q<0則P在Q的逆時針方向。

【計算幾何+極角排序+爆ll】E. Convex