CF607 E

题意:
给出平面上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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值