zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    poj 1392 Ouroboros Snake
    poj 1780 Code
    poj 2513 Colored Sticks
    ZOJ 1455 Schedule Problem(差分约束系统)
    poj 3169 Layout (差分约束)
    ZOJ1260/POJ1364国王(King)
    poj 1201/zoj 1508 intervals 差分约束系统
    zoj 2770 Burn the Linked Camp (差分约束系统)
    构造函数和析构函数
    PHP面向对象——静态属性和静态方法
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14618765.html
Copyright © 2011-2022 走看看