题目
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);
}