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)了。
复杂度O(nlog2n+m)

0 0
原创粉丝点击