【二分+计算几何+树状数组】【CF1446F】Line Distance

题目

http://codeforces.com/contest/1446/problem/F

题目大意

平面上有 n n n 个点,将这些点两两连线,问这些直线到原点的最大距离

n < = 1 0 5 n<=10^5 n<=105

思路

考虑二分答案
如果一条直线和原点的距离 > = a n s >=ans >=ans 则这条直线与以原点为圆心, a n s ans ans 为半径的园不相交(可能相切)

对于每个点向圆引两条切线。可以证明,如果两个点所连出来的直线和圆不相交,当且仅当这两个点所对应的切点连成的直线相交。

证明的话……emm……放几张官方题解的图感受一下?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那么我们可以用树状数组维护这些切点,查询也很容易

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double pi = acos(-1.0);
const int N=1e5+77;
int n;
struct P 
{
	int x,y;
	double a,d;
};
P p[N];
struct node 
{
	double x,l,r;
	int n;
};
bool comp(node a,node b) 
{
	return a.x < b.x;
}
int bit[N];
int update(int i,int num) 
{
	for(i++; i <= n; i += i & (-i)) bit[i] += num;
	return 0;
}
int query(int i) {
	int sum = 0;
	for(i++; i > 0; i -= i & (-i)) sum += bit[i];
	return sum;
}
node A[N<<1];
double all[N];
int main() 
{
	ll k;
	scanf("%d%lld",&n,&k);
	for(int i = 0; i < n; i++) {
		scanf("%d%d",&p[i].x,&p[i].y);
		p[i].a = atan2(p[i].y,p[i].x);
		p[i].d = sqrt(p[i].x*p[i].x+p[i].y*p[i].y);
	}
 
	int t = 0;
	double l = 0,r = 1.5e4;
	while (t < 40)
	{
		t++;
		double m = (l+r) / 2;
 
		ll num = 0;
		int c = 0,c2 = 0;
		for(int i = 0; i < n; i++) 
		{
			if (p[i].d > m) {
				double t = acos(m/p[i].d);
				double a1 = p[i].a-t,a2 = p[i].a+t;
				while (a1 < -pi) a1 += 2*pi;
				while (a2 > pi) a2 -= 2*pi;
				if (a1 > a2) swap(a1,a2);
				A[c++] = (node){a1,a2,a2,0},all[c2++] = a2;
				A[c++] = (node){a1-1e-12,a1+1e-12,a2-1e-12,1};
			}
		}
		fill(bit,bit+n+1,0);
		sort(A,A+c,comp);
		sort(all,all+c2);
		for(int i = 0; i < c; i++) {
			if (A[i].n == 0) {
				int p = lower_bound(all,all+c2,A[i].l)-all;
				update(p,1);
			}
			else {
				int q = upper_bound(all,all+c2,A[i].r)-all-1;
				int p = lower_bound(all,all+c2,A[i].l)-all;
				num += A[i].n*(query(q)-query(p-1));
			}
		}
		num = (ll) n*(n-1)/2-num;
		if (num < k) l = m;
		else r = m;
	}
	printf("%.12lf\n",l);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值