LOJ 2882. 「JOISC 2014 Day4」兩個人的星座
阿新 • • 發佈:2020-11-02
LOJ 2882. 「JOISC 2014 Day4」兩個人的星座
對於任意兩個凸多邊形相離,一定可以找到一條直線將它們分在平面的兩個區域
而對於三角形的情況更為特殊
分析可以發現,很難直接列舉三角形外直線計算,而對於任意的兩個合法的三角形,在其6點中較近的4個點中
一定可以從兩個三角形中各選一個點,連出兩條交錯的合法的分界線,例如下圖
那麼可以考慮列舉這樣的一條直線,即確定了兩個分界線上的端點,然後從兩個半平面內選出不同顏色的點
直接列舉,然後\(O(n)\)數出這樣的點,複雜度為\(O(n^3)\)
顯然可以想到列舉一個頂點,然後對於其他極角排序,旋轉另一個點,同步統計半平面內的點個數,複雜度為\(O(n^2\log n)\)
實現上,可以列舉一個點,尺取一個半平面內的點
#include<bits/stdc++.h> using namespace std; using ll=int64_t; enum{N=3010}; int n,X[N],Y[N],I[N],C[N],c,s[2][3],i; double T[N]; ll ans; ll E(int j,int k){ return 1ll*(X[j]-X[i])*(Y[k]-Y[i])-1ll*(Y[j]-Y[i])*(X[k]-X[i]); } int main(){ for(int i=scanf("%d",&n);i<=n;++i) scanf("%d%d%d",X+i,Y+i,C+i); for(i=1;i<=n;++i) { c=0; memset(s,0,sizeof s); for(int j=1;j<=n;++j) if(i!=j) I[++c]=j,T[j]=atan2(Y[j]-Y[i],X[j]-X[i]),s[0][C[j]]++; sort(I+1,I+n,[&](int x,int y){ return T[x]<T[y]; }); int p=1; for(int j=1;j<n;++j) { while(E(I[j],I[p])>=0) { s[0][C[I[p]]]--,s[1][C[I[p]]]++; p=p%c+1; if(p==j) break; } ans+=1ll*s[0][(C[i]+1)%3]*s[0][(C[i]+2)%3]*s[1][(C[I[j]]+1)%3]*s[1][(C[I[j]]+2)%3]; s[1][C[I[j]]]--,s[0][C[I[j]]]++; } } cout<<ans/2; }
一個更好的寫法是把在\(y\)軸以下的點中心對稱上來,統計時每跨過一個點改變一次
#include<bits/stdc++.h> using namespace std; enum{N=3010}; int n,X[N],Y[N],C[N],c,s[2][3],i,j,d,a,b,x,y; int64_t ans; struct Node{ int x,y,d,c; Node(){} Node(int p,int q,int r) { x=p,y=q,c=r,d=0; if(y<0||(x<0&&y==0)) d=1,x=-x,y=-y; s[d][c]++; } } A[N]; struct cmp{ int operator () (const Node &a,const Node &b){ return 1ll*a.x*b.y<1ll*a.y*b.x; }}; int main(){ for(i=scanf("%d",&n);i<=n;++i) scanf("%d%d%d",X+i,Y+i,C+i); for(i=1;i<=n;++i) { memset(s,c=0,sizeof s),a=(C[i]+1)%3,b=(a+1)%3; for(j=1;j<=n;++j) if(i!=j) A[++c]=Node(X[j]-X[i],Y[j]-Y[i],C[j]); for(sort(A+1,A+n,cmp()),j=1;j<n;++j) { s[A[j].d][c=A[j].c]--,x=(c+1)%3,y=(x+1)%3; for(d=0;d<2;++d) ans+=1ll*s[d][a]*s[d][b]*s[!d][x]*s[!d][y]; s[!A[j].d][c]++; } } cout<<ans/4; }