CF1446F. Line Distance

题目大意

给出n个点,求n(n-1)/2条直线据原点距离中第k小的

n<=1e5,k<=n(n-1)/2

题解

结论题

首先二分答案,变成判断一条直线AB是否与圆有交

结论:直线AB与圆有交,当且仅当A的切点连线A1A2和B的切点连线B1B2不相交

证明感受一下



所以直接离散化+树状数组即可

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define M_PI 3.14159265358979323846
#define abs(x) ((x)>0?(x):-(x))
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define low(x) ((x)&-(x))
#define E 0.000000001
#define ll long long
//#define file
using namespace std;

struct type{double x;int id;} b[200001];
struct Type{int x,y;} c[100001];
double X[100001],Y[100001],dis[100001],S[100001],L,R,Mid;
int tr[200001],n,i,j,k,l;
ll K;

void change(int t,int s) {while (t<=n*2) tr[t]+=s,t+=low(t);}
int find(int t) {int ans=0; while (t) ans+=tr[t],t-=low(t);return ans;}
bool cmp(type a,type b) {return a.x<b.x;}
bool Cmp(Type a,Type b) {return a.x<b.x;}
bool pd(double r)
{
	double s1,s2,ss,s;
	ll sum=0;
	
	l=0;
	fo(i,1,n)
	if (dis[i]>r)
	{
		s=acos(r/dis[i]);
		s1=S[i]-s,s2=S[i]+s;
		if (s1<0) s1+=M_PI*2;
		if (s2>M_PI*2) s2-=M_PI*2;
		if (s1>s2) ss=s1,s1=s2,s2=ss;
		b[++l]={s1,-i},b[++l]={s2,i};
	}
	
	if (l)
	{
		fo(i,1,n) c[i]={1,0};
		sort(b+1,b+l+1,cmp);
		fo(i,1,l)
		{
			if (b[i].id<0) c[-b[i].id].x=i;
			else c[b[i].id].y=i;
		}
		
		l=0;
		fo(i,1,n) if (c[i].x<=c[i].y) c[++l]=c[i];
		sort(c+1,c+l+1,Cmp);
		memset(tr,0,sizeof(tr));
		fd(i,l,1)
		{
			sum+=find(c[i].y);
			change(c[i].x,1),change(c[i].y,-1);
		}
	}
	sum=1ll*n*(n-1)/2-sum;
	return sum>=K;
}

int main()
{
	#ifdef file
	freopen("CF1446F.in","r",stdin);
	#endif
	
	scanf("%d%lld",&n,&K);
	fo(i,1,n)
	{
		scanf("%lf%lf",&X[i],&Y[i]),dis[i]=sqrt(X[i]*X[i]+Y[i]*Y[i]),S[i]=atan2(Y[i],X[i]);
		if (S[i]<0) S[i]+=M_PI*2;
	}
	L=0,R=20000;
	while (R-L>E)
	{
		Mid=(L+R)/2;
		if (!pd(Mid))
		L=Mid; else R=Mid;
	}
	printf("%.15lf
",L);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13991695.html