CF607 E
来源:互联网 发布:mysql删除前100条数据 编辑:程序博客网 时间:2024/06/05 13:25
题意:
给出平面上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)了。
复杂度
0 0