题解:
二分答案,然后检查当前范围(圆)中直线的交点是否超过
m
m
。
在圆中检查两条直线相交非常方便,只需要把直线与圆的交点做出来,相交直线一定形如的形式。
那么排一边序直接做扫描线,用树状数组维护区间和。
最后查询的时候要查询 l−eps l − e p s ,因为 l l <script type="math/tex" id="MathJax-Element-194">l</script>的点可能很多。
#include <bits/stdc++.h>
typedef double LD;
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
int n,m;
LD ans;
struct P{
LD x,y;
P(LD x=0,LD y=0):x(x),y(y){}
friend inline P operator -(const P &a,const P &b) {return P(a.x-b.x,a.y-b.y);}
inline P rev() {return P(-x,-y);}
inline P rev(LD o) {
LD sn=sin(o),cs=cos(o);
return P(x*cs-y*sn,x*sn+y*cs);
}
inline LD dis() {return sqrt(x*x+y*y);}
friend inline LD operator *(const P &a,const P &b) {return a.x*b.y-a.y*b.x;}
}st,line[50010];
bool onleft[50010];
inline bool OnLeft(int i) {
P a=P(0,line[i].y),b=P(1,line[i].x+line[i].y);
return (a-st)*(b-st)>0;
}
typedef pair<LD,LD> pii;
const LD PI=acos(-1.0);
const LD PI2=acos(-1.0)*2.0;
inline LD fix(LD x) {
while(x<-PI) x+=PI2;
while(x>PI) x-=PI2;
return x;
}
pii stk[100010];
int tot,bit[100010];
inline int inc(int i,int v) {for(;i<=tot;i+=(i&(-i))) bit[i]+=v;}
inline int ask(int i,int rs=0) {for(;i;i-=(i&(-i))) rs+=bit[i]; return rs;}
inline int calc(LD r) {
tot=0;
for(int i=1;i<=n;i++) {
LD dis=fabs(line[i].x*st.x-st.y+line[i].y)/sqrt(line[i].x*line[i].x+1.0);
if(dis>r) continue;
LD ang=asin(dis/r);
ang=fix(ang);
if(ang>PI/2) ans=PI/2-ang;
P tar(100,100*line[i].x);
if(onleft[i]) tar=tar.rev();
P t1=tar.rev(ang).rev();
P t2=tar.rev(-ang);
LD a1=fix(atan2(t1.y,t1.x));
LD a2=fix(atan2(t2.y,t2.x));
if(a1>a2) swap(a1,a2);
stk[++tot]=make_pair(a1,a2);
stk[++tot]=make_pair(a2,a1);
}
sort(stk+1,stk+tot+1);
for(int i=1;i<=tot;i++) bit[i]=0;
long long cnt=0;
for(int i=1;i<=tot;i++) {
if(stk[i].second>stk[i].first) inc(i,1);
else {
int p=lower_bound(stk+1,stk+tot+1,make_pair(stk[i].second,stk[i].first))-stk;
cnt+=ask(i)-ask(p);
inc(p,-1);
}
} return cnt;
}
set < pair<LD,int> > S;
typedef set< pair<LD,int> >::iterator it;
inline it nxt(it p) {return ++p;}
inline P inter(int i,int j) {
LD x=(line[j].y-line[i].y)/(line[i].x-line[j].x);
return P(x,line[i].x*x+line[i].y);
}
inline LD calc_v(LD r) {
tot=0; LD cnt=0;
for(int i=1;i<=n;i++) {
LD dis=fabs(line[i].x*st.x-st.y+line[i].y)/sqrt(line[i].x*line[i].x+1.0);
if(dis>r) continue;
LD ang=asin(dis/r);
ang=fix(ang);
if(ang>PI/2) ans=PI/2-ang;
P tar(100,100*line[i].x);
if(onleft[i]) tar=tar.rev();
P t1=tar.rev(ang).rev();
P t2=tar.rev(-ang);
LD a1=fix(atan2(t1.y,t1.x));
LD a2=fix(atan2(t2.y,t2.x));
if(a1>a2) swap(a1,a2);
stk[++tot]=make_pair(a1,i+233);
stk[++tot]=make_pair(a2,a1);
}
sort(stk+1,stk+tot+1);
for(int i=1;i<=tot;i++) {
if(stk[i].second>stk[i].first) S.insert(make_pair(stk[i].first,(int)(stk[i].second-233+0.1)));
else {
it p=S.lower_bound(make_pair(stk[i].second,0));
if(p==S.end()) continue;
for(it t=nxt(p);t!=S.end();t++)
cnt+=(inter(t->second,p->second)-st).dis();
S.erase(p);
}
} return cnt;
}
int main() {
n=rd();
st.x=rd()/1000.0,st.y=rd()/1000.0;
m=rd();
for(int i=1;i<=n;i++)
line[i].x=rd()/1000.0, line[i].y=rd()/1000.0;
for(int i=1;i<=n;i++) onleft[i]=OnLeft(i);
LD l=0,r=1e10; int c;
for(int i=1;i<=70;i++) {
LD mid=(l+r)/2.0;
if((c=calc(mid))>=m) r=mid;
else l=mid;
}
c=calc(l-(1e-7));
printf("%.6f\n",(double)(calc_v(l-(1e-7))+(m-c)*l));
}