题意:
给出平面上n条直线,记它们交点的的点集为s(可重)。给出点p(x,y),询问s中与p欧几里得距离前m近的点和p的距离和。
n<=50000
m<=30000000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 110000
#define eps 1e-18
#define lowbit(x) (x&(-x))
using namespace std;
struct point{long double x,y;int o,id;}A[N];
struct line{long double k,b;}a[N];
struct cir{long double p,q,r;}c;
struct node{int pre,nex;}b[N];
int n,m,lt[N],las,num,fir[N];
long double ans;
bool cmp(point x,point y)
{
if(x.o<y.o) return 1;
if(x.o>y.o) return 0;
if(x.o<=2) {if(x.x>y.x) return 1;}
else {if(x.x<y.x) return 1;}
return 0;
}
long double dis(long double x1,long double y1,long double x2,long double y2)
{
long double t1=x1-x2,t2=y1-y2;
return sqrt(t1*t1+t2*t2);
}
bool cir_line(cir c,line l,long double &x1,long double &y1,long double &x2,long double &y2)
{
long double p=c.p,q=c.q,r=c.r,k=l.k,b=l.b,A,B,C;
A=k*k+1;
B=2*k*b-2*p-2*q*k;
C=p*p+b*b+q*q-2*q*b-r*r;
long double d=B*B-4*A*C;
if(d<0) return 0;
d=sqrt(d);
x1=(-B+d)/(2*A);
x2=(-B-d)/(2*A);
y1=x1*k+b;
y2=x2*k+b;
}
void line_line(line a,line b)
{
if(a.k==b.k) return;
long double x=(b.b-a.b)/(a.k-b.k),y=a.k*x+a.b;
ans+=dis(x,y,c.p,c.q);
}
void get_point()
{
num=0;
for(int i=1;i<=n;i++)
{
bool bo=0;long double x1,y1,x2,y2;
bo=cir_line(c,a[i],x1,y1,x2,y2);
if(bo) A[++num].x=x1,A[num].y=y1,A[num].id=i,A[++num].x=x2,A[num].y=y2,A[num].id=i;
}
for(int i=1;i<=num;i++)
if(A[i].y>c.q) {if(A[i].x>c.p) A[i].o=1;else A[i].o=2;}
else{if(A[i].x<c.p) A[i].o=3;else A[i].o=4;}
sort(A+1,A+num+1,cmp);
}
void change(int x,int d)
{
for(int i=x;i<=num;i+=lowbit(i)) lt[i]+=d;
}
int find(int x)
{
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=lt[i];
return sum;
}
int solve()
{
get_point();
for(int i=1;i<=num;i++) lt[i]=fir[A[i].id]=0;
int t=0;
for(int i=1;i<=num;i++)
{
int x=A[i].id;
if(fir[x]==0) fir[x]=i,change(i,1);
else
{
t+=find(i)-find(fir[x]);
change(fir[x],-1);
if(t>m) break;
}
}
return t;
}
void ins(int x)
{
b[x].pre=las;b[las].nex=x;
las=x;
}
void del(int x)
{
if(las==x) las=b[x].pre;
b[b[x].pre].nex=b[x].nex;
b[b[x].nex].pre=b[x].pre;
}
void cal()
{
get_point();
for(int i=1;i<=num;i++) fir[A[i].id]=0;
las=0;
for(int i=1;i<=num;i++)
{
int x=A[i].id;
if(fir[x]==0) fir[x]=i,ins(x);
else
{
for(int j=las;j!=x;j=b[j].pre) line_line(a[x],a[j]);
del(x);
}
}
}
int main()
{
//freopen("data.txt","r",stdin);
long double p,q;
scanf("%d",&n);
cin>>p>>q;scanf("%d",&m);
p/=1000;q/=1000;
for(int i=1;i<=n;i++)
{
int k,b;scanf("%d%d",&k,&b);
a[i].k=(long double)k/1000;a[i].b=(long double)b/1000;
}
c.p=p;c.q=q;
long double l=0,r=1,k=0;int t=0;
while(1)
{
c.r=r;int t=solve();
if(t>=m) break;
r*=2;
}
for(int z=1;z<=100;z++)
{
long double mid=(l+r)/2;
c.r=mid;
int tt=solve();
if(tt<m) k=mid,t=tt,l=mid+eps;
else r=mid-eps;
}
c.r=k;
cal();
ans+=(m-t)*k;
printf("%.9lf\n",(double)ans);
return 0;
}
题解:
容易想到二分,画出一个圆,然后膜题解了。。
考虑两条直线交点在圆内,那么和圆周的交点一定是ABAB的形式,然后就可以统计了(似乎在哪里见过这种套路?)
最后算答案用链表就可以做到O(m)了。
复杂度
O(nlog2n+m)