← 回到所有文章

CF645G Armistice Area Apportionment 题解

题意 给定平面上 n 个点和 a。设 P(a,0),Q(-a,0)。要求找出一条直线 l 过至少两个给定点,最小化 max{ PX-QX},Xin l。 nle10^5,ale 10^4。 题解 计算…

题解文章的简约插画封面

题意

给定平面上 nn 个点和 aa。设 P(a,0),Q(−a,0)P(a,0),Q(-a,0)。要求找出一条直线 ll 过至少两个给定点,最小化 max⁡{∣PX−QX∣},X∈l\max\{ |PX-QX|\},X\in l。

n≤105,a≤104n\le10^5,a\le 10^4。

题解

计算

首先我们来看看,假如确定某两个点 max⁡{∣PX−QX∣},X∈l\max\{ |PX-QX|\},X\in l 这个东西是怎么算的,根据将军饮马的知识

第一种情况 PQPQ 在 ll 同侧,观察 ΔPQX\Delta PQX 可得,PX−QX≤PQPX-QX\leq PQ,所以可得,max⁡{∣PX−QX∣}=PQ,X∈l\max\{ |PX-QX|\}=PQ,X\in l。

第二种情况 PQPQ 在 ll 异侧,故做 PP 关于 ll 的对称点 P′P',观察 ΔP′QX\Delta P'QX 可得,PX−QX=P′X−QX≤P′QPX-QX=P'X-QX\leq P'Q,所以可得,max⁡{∣PX−QX∣}=P′Q,X∈l\max\{ |PX-QX|\}=P'Q,X\in l。

解题

由于答案最大就是 2a2a,所以不需要特别考虑第二种情况。现在只计算第一种情况,最大最小值直接套路用二分答案,若当前二分 midmid 这个答案,考虑怎么 check。这样二分的几何意义就是某一个 P′P' 点在以 QQ 为圆心,半径为 midmid 圆内,记这个圆为圆 QQ。进而考虑 P′P' 怎么转化,就是以 AA 为圆心 APAP 为半径的圆和以 BB 为圆心 BPBP 为半径的圆的其中一个交点,分别记为圆 A,BA,B,那么这样转化的好处是把 A,BA,B 贡献拆分开了,两个圆是独立的。现在记圆 QQ 在圆 A,BA,B 内的弧分别为 A⌢,B⌢\overset{\LARGE{\frown}}{A},\overset{\LARGE{\frown}}{B},那么两个圆的交点在 QQ 可以转化为 A⌢\overset{\LARGE{\frown}}{A} 和 B⌢\overset{\LARGE{\frown}}{B} 是有交,但是可能圆 A,BA,B 是包含关系,所以 A⌢\overset{\LARGE{\frown}}{A} 和 B⌢\overset{\LARGE{\frown}}{B} 不能是包含关系。这个很好判和括号序差不多,求出圆 A,BA,B 和圆 QQ 的交点和圆心到交点的向量幅角,用栈判断就可以了。这个过程不好理解可以看我画的 GeoGebra。

#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE int
#define OUTPUT_DATA_TYPE int
inline __attribute((always_inline)) INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){if(x<0)x=-x,putchar('-');if(x>9)print(x/10);putchar(x%10^48);return;}

struct point{
	double x,y;
	point operator + (const point &o) const {return {x+o.x,y+o.y};}
	point operator - (const point &o) const {return {x-o.x,y-o.y};}
	point operator * (const double &o) const {return {x*o,y*o};}
};

struct circle{
	point o;
	double r;
}a[100010];

double moo(point a){return std::sqrt(a.x*a.x+a.y*a.y);}
double dist(point a,point b){return moo(a-b);}
point unit(point a){return a*(1.0/moo(a));}
double polarAngle(point a){return atan2(a.y,a.x);}

auto intersect(circle a,circle b){
	double d=dist(a.o,b.o);
	double x=(a.r*a.r+d*d-b.r*b.r)/(d*2);
	double z=std::sqrt(a.r*a.r-x*x);
	point u=unit(b.o-a.o);
	point mid=a.o+u*x;
	std::swap(u.x,u.y),u.x*=-1;
	return std::make_pair(mid+u*z,mid-u*z);
}

point p,q;
int n;

bool check(double mid){
	register int i;
	register double d;
	std::vector<std::pair<double,int>> vec;
	std::stack<int> S;
	circle o={p,mid};
	for(i=0;i<n;++i){
		d=dist(o.o,a[i].o);
		if(o.r+a[i].r>d&&fabs(o.r-a[i].r)<d){
			auto tmp=intersect(o,a[i]);
			vec.push_back({polarAngle(tmp.first-o.o),i});
			vec.push_back({polarAngle(tmp.second-o.o),i});
		}
	}
	std::sort(vec.begin(),vec.end());
	for(auto val:vec)
		if(S.empty()||S.top()!=val.second) S.push(val.second);
		else S.pop();
	return !S.empty();
}

int main(){
	#ifndef ONLINE_JUDGE
	freopen("name.in", "r", stdin);
	freopen("name.out", "w", stdout);
	#endif

	register int i;
	register double l,r,mid;
	n=read();
	int aa=read();

	p.x=aa,q.x=-aa;

	for(i=0;i<n;++i) a[i].o.x=read(),a[i].o.y=read(),a[i].r=dist(a[i].o,q);
	l=0,r=dist(p,q);
	for(i=0;i<50;++i){
		mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid;
	}

	printf("%.10lf",l);

	#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	#endif
    return 0;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭