[COCI2009]Dvapravca 計算幾何
阿新 • • 發佈:2019-01-08
由於 cpp 計算 -a 否則 name space 要求 problem 。
[COCI2009]Dvapravca
LG傳送門
先給出考場上的\(O(n^3)\)亂搞方法:枚舉一個藍點和一個紅點,找出過著兩個點的直線,再枚舉藍點找出這條直線最多能往兩邊擴展多寬,最後枚舉紅點計算貢獻。
註意在確定一條直線能往兩邊擴展多寬時不要求點到直線的距離,否則常數會太大,只要求豎直方向的距離就可以了。正確性顯然,具體看代碼。
現在有個重要的問題,求大佬來解決:如果直接這樣寫會被卡到65到70分,但是如果把藍點和紅點按\(x\)為第一關鍵字\(y\)為第二關鍵字從小到大排個序,就能得到90至100分(考試時用的評測機能跑到90分,洛谷神機上能切)。然而我不會證明這樣究竟優化在哪裏,求大佬證明。
#include<cstdio> #include<algorithm> #define R register #define I inline #define D double using namespace std; const int N=1003,inf=0x3f3f3f3f; struct V{int x,y;}r[N],b[N]; struct L{D k,b;}f; I int operator<(V a,V b){return a.x^b.x?a.x<b.x:a.y<b.y;} I D min(D x,D y){return x<y?x:y;} I int max(int x,int y){return x>y?x:y;} I L lin(V a,V b){ if(a.x==b.x) return (L){inf,0}; D k=(D)(b.y-a.y)/(b.x-a.x); return (L){k,(D)a.y-k*a.x}; } I D dst(V a){return f.k*a.x+f.b-a.y;} int main(){ R int n,x,y,i,j,t,p=0,q=0,g,h,o=0; R char c[2]; D d,u,v; scanf("%d",&n); for(i=1;i<=n;++i){ scanf("%d%d%s",&x,&y,c); if(c[0]=='R') r[++p]=(V){x,y}; else b[++q]=(V){x,y}; } sort(b+1,b+q+1),sort(r+1,r+p+1);//就是這裏,不加會慢很多 for(i=1;i<=q;++i) for(j=1;j<=p;++j){ f=lin(b[i],r[j]),u=v=inf,g=h=1; for(t=1;t<=q;++t) if(t^i) if((d=dst(b[t]))>0) u=min(u,d); else v=min(v,-d); for(t=1;t<=p;++t) if(t^j) if((d=dst(r[t]))>0) g+=d<u; else h+=-d<v; o=max(o,max(g,h)); } printf("%d",o); return 0; }
親測如果沒加sort應該是過不了的,xzz大神仙嘗試把坐標系隨機旋轉了一下,變得更快了。所以這道題告訴我們一個經驗:碰見不會的計算幾何題,先隨機旋轉坐標系,再把點排一遍序,打起暴力更有自信!
還是放一下正解\(O(n^2 log n)\)的做法吧。考慮如果要求直線垂直於\(x\)軸,那麽如果把點按剛才所說的排一遍序,要求的就是序列上連續的最長的\(1\)的個數(如果把紅色視作\(1\))。由於直線可以傾斜,考慮旋轉坐標系,當某兩個點連線的斜率與當前\(y\)軸在原坐標系中的斜率相等時,這兩個點在序列上的位置就會交換,這下可以用線段樹來維護,由於最多交換\(O(n^2)\)次順序,復雜度是\(O(n^2 log n)\)
#include<cstdio> #include<algorithm> #define R register #define I inline #define D double using namespace std; const int N=1003,M=1000003,S=4003; int f[N],n; struct T{int f,l,r,d;}e[S]; struct V{int x,y,c;}p[N]; struct L{D k; int x,y;}q[M]; I int operator<(V a,V b){return a.x^b.x?a.x<b.x:a.y<b.y;} I int operator<(L a,L b){return a.k>b.k;} I int max(int x,int y){return x>y?x:y;} I T operator+(T x,T y){ T z; z.f=max(x.r+y.l,max(x.f,y.f)),z.d=x.d+y.d; if(x.l==x.d) z.l=x.d+y.l; else z.l=x.l; if(y.r==y.d) z.r=x.r+y.d; else z.r=y.r; return z; } I void swp(int&x,int&y){x^=y,y^=x,x^=y;} I void upd(int k,int v){e[k]=(T){v,v,v,1};} I void bld(int k,int l,int r){ if(l==r){ upd(k,p[l].c); return ; } R int p=k<<1,q=p|1,m=l+r>>1; bld(p,l,m),bld(q,m+1,r),e[k]=e[p]+e[q]; } void mdf(int k,int l,int r,int x,int v){ if(l==r){ upd(k,v); return ; } R int p=k<<1,q=p|1,m=l+r>>1; if(x<=m) mdf(p,l,m,x,v); else mdf(q,m+1,r,x,v); e[k]=e[p]+e[q]; } int main(){ R int i,j,x,y,t=0,o; R char c[2]; scanf("%d",&n); for(i=1;i<=n;++i){ scanf("%d%d%s",&x,&y,c); if(c[0]=='R') p[i]=(V){x,y,1}; else p[i]=(V){x,y,0}; } sort(p+1,p+n+1); for(i=1;i<=n;++i) f[i]=i; for(i=1;i<n;++i) for(j=i+1;j<=n;++j) q[++t]=(L){(D)(p[j].y-p[i].y)/(p[j].x-p[i].x),i,j}; sort(q+1,q+t+1),bld(1,1,n),o=e[1].f; for(i=1;i<=t;++i){ x=f[q[i].x],y=f[q[i].y],swp(f[q[i].x],f[q[i].y]); if(p[x].c^p[y].c) swp(p[x].c,p[y].c),mdf(1,1,n,x,p[x].c),mdf(1,1,n,y,p[y].c),o=max(o,e[1].f); } printf("%d",o); return 0; }
[COCI2009]Dvapravca 計算幾何