1. 程式人生 > >[BZOJ2961]共點圓-待解決

[BZOJ2961]共點圓-待解決

cstring top for font zoj string namespace https --

Description

傳送門

Solution

考慮對於每一個點:

設圓的坐標為(x,y),點的坐標為(x0,y0)。依題意得,當一個點在圓裏,需要滿足(x-x0)2+(y-y0)2<=x2+y2

化簡得x02+y02<=2x0*x+2y0*y。

當y0>0,x*(-x0/y0)+0.5y0+x02/(2*y0)<=y,這是一個半平面的式子;當y0<0時同理,但是要變號。

所以對於某個點(x0,y0),我們構造出在它前面所有圓心的凸包。凸包應分為上下。

通過以上式子我們可以得出,當y0>0時應在下凸包上找點(x,y)【該點為直線y=-x0/y0與下凸包的切點,即若此點滿足要求,其他任何點都會滿足要求。通過這個條件,我們也可以理解把該點理解為2x0*x+2y0*y最小的點】,反之則應該在上凸包上找。

好的讓我們假設目前的y0>0,可證明如果將下凸包上的點(x,y)從左到右計算,x*x0+y*y0是單峰的(這個式子是向量的點乘,向量a點乘向量b=a的長度*b的長度*cos(向量a,b的夾角)。所以根據這個定義,證明,就畫圖吧。qaq正兒八經公式太麻煩了。)上凸包也是一樣的。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const double eps=1e-11
; int n; bool ans[500010]; struct W{ int tp;double x,y; friend double operator *(W a,W b){return a.x*b.y-a.y*b.x;} friend double operator ^(W a,W b){return a.x*b.x+a.y*b.y;} friend W operator -(W a,W b){return W{0,a.x-b.x,a.y-b.y};} friend bool operator <(W a,W b){return (fabs(a.x-b.x)<eps)?a.y<b.y:a.x<b.x;} }w[
500010],t[500010],st[2][500010];//st[0]-上凸包,st[1]-下凸包 double ask(W a,W b){return a.x*b.x+a.y*b.y;} bool _ok[500010]; int top0,top1; bool query(int id) { int l,r,mid1,mid2;double minn=1e12; if (w[id].y<0) { l=1;r=top0; while (r-l>2) { mid1=(l*2+r)/3;mid2=(r*2+l)/3; if ((w[id]^st[0][mid1])<(w[id]^st[0][mid2])) r=mid2; else l=mid1; } for (int i=l;i<=r;i++) minn=min(minn,w[id]^st[0][i]); } else { l=1;r=top1; while (r-l>2) { mid1=(l*2+r)/3;mid2=(r*2+l)/3; if ((w[id]^st[1][mid1])<(w[id]^st[1][mid2])) r=mid2; else l=mid1; } for (int i=l;i<=r;i++) minn=min(minn,w[id]^st[1][i]); } if (2*minn-(w[id].x*w[id].x+w[id].y*w[id].y)<eps) ans[id]=0; } void solve(int l,int r) { if (l==r) return; int mid=(l+r)/2,tot=0; solve(l,mid); solve(mid+1,r); for (int i=l;i<=mid;i++) if (!w[i].tp) t[++tot]=w[i]; sort(t+1,t+tot+1); top1=0,top0=0; for (int i=1;i<=tot;i++) { while (top0>1&&(st[0][top0]-st[0][top0-1])*(t[i]-st[0][top0])>-eps) top0--; st[0][++top0]=t[i]; } st[0][top0+1].x=st[0][top0].x;st[0][top0+1].y=-1e12; for (int i=tot;i;i--) { while (top1>1&&(st[1][top1]-st[1][top1-1])*(t[i]-st[1][top1])>-eps) top1--; st[1][++top1]=t[i]; } st[1][top1+1].x=st[1][top1].x;st[1][top1+1].y=1e12; for (int i=mid+1;i<=r;i++) if (w[i].tp&&ans[i]) query(i); } bool _is=0; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%d%lf%lf",&w[i].tp,&w[i].x,&w[i].y); if (!w[i].tp) _is=1;else ans[i]=_is; } solve(1,n); for (int i=1;i<=n;i++) if (w[i].tp) if (ans[i]) printf("Yes\n");else printf("No\n"); }

[BZOJ2961]共點圓-待解決