• CF1446FLine Distance【计算几何,树状数组,二分】


    正题

    题目链接:https://www.luogu.com.cn/problem/CF1446F


    题目大意

    给出\(n\)个点,求所有点对构成的直线中与原点距离第\(k\)小的距离

    \(2\leq n\leq 10^5,1\leq k\leq \frac{n(n-1)}{2}\)


    解题思路

    二分还是挺显然的,考虑二分了之后怎么判断一个距离以内的直线数量

    两个点对之间的直线在原点距离\(d\)以内,也就是这条直线经过原点为中心半径为\(d\)的圆。换一种理解,也就是如果有这个圆那么这两个点对之间无法相互看见。

    现在问题变为了求有多少个点对之间无法相互看见,给张官方题解的图片就挺容易理解的
    在这里插入图片描述
    也就是说我们对于每个点找到它与圆的两个切点之间在圆上构成的一个区间。

    如果两个点的区间有交集那么他们就可以相互看见。

    现在问题就变为了求有多少对区间有交。

    这个直接用树状数组统计就好了。

    环的话直接断开,跨越了环的就取反。


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define lowbit(x) (x&-x)
    using namespace std;
    const ll N=2e5+10;
    double eps=1e-8,Pi=acos(-1);
    ll n,k,cnt,m,t[N],p[N],l[N],r[N];
    double a[N],b[N],c[N],L[N],R[N];
    bool v[N];
    void Change(ll x,ll val){
    	while(x<=cnt){
    		t[x]+=val;
    		x+=lowbit(x);
    	}
    	return;
    }
    ll Ask(ll x){
    	ll ans=0;
    	while(x){
    		ans+=t[x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    bool cmp(ll x,ll y)
    {return l[x]<l[y];}
    ll check(double d){
    	cnt=0;m=0;
    	for(ll i=1;i<=n;i++)v[i]=0;
    	for(ll i=1;i<=n;i++){
    		double dis=sqrt(a[i]*a[i]+b[i]*b[i]);
    		if(dis-d<eps){v[i]=1;continue;}
    		double ang=atan2(b[i],a[i]),ta=acos(d/dis);
    		L[i]=ang-ta;R[i]=ang+ta;
    		if(L[i]<-Pi)L[i]+=2*Pi;
    		if(R[i]>Pi)R[i]-=2*Pi;
    		if(L[i]>R[i])swap(L[i],R[i]);
    		c[++cnt]=L[i];c[++cnt]=R[i]; 
    	}
    	sort(c+1,c+1+cnt);
    	for(ll i=1;i<=n;i++){
    		if(v[i])continue;p[++m]=i;
    		l[i]=lower_bound(c+1,c+1+cnt,L[i])-c;
    		r[i]=lower_bound(c+1,c+1+cnt,R[i])-c;
    	}
    	memset(t,0,sizeof(t));
    	sort(p+1,p+1+m,cmp);
    	ll ans=n*(n-1)/2;
    	for(ll i=1;i<=m;i++){
    		ll x=p[i];
    		ans-=Ask(r[x])-Ask(l[x]-1);
    		Change(r[x],1);
    	}
    	return ans; 
    }
    signed main()
    {
    	scanf("%lld%lld",&n,&k);
    	for(ll i=1;i<=n;i++)
    		scanf("%lf%lf",&a[i],&b[i]);
    	double l=0,r=20000;
    	while(r-l>=eps){
    		double mid=(l+r)/2.0;
    		if(check(mid)<k)l=mid;
    		else r=mid;
    	}
    	printf("%.10lf",l);
    	return 0;
    }
    
  • 相关阅读:
    colemak,你用了吗?
    DELPHI一个对付内存汇漏的办法和技巧
    使用for in 循环数据集
    今天差点被断电搞死了,幸好IDE的备份救了我
    为什么继续选择DELPHI?
    无缘DELPHI的BUG
    DELPHI DOUBLE不解之迷
    失败的大牛事件委托,与我的委托
    2017-02-08 01:19:09 Scrapy: pk5_mylist
    2016-09-02 16:50:15 webqq 协议分析
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14618765.html
Copyright © 2020-2023  润新知