把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF575E】Spectator Riots(结论题+凸包)

点此看题面

  • 一张二维平面上,全集为整点集\(S=\{(x,y)|x,y\in[0,10^5]\}\)
  • 另有\(n\)个整点集\(P_i=S\cap\{(x,y)||x-x_i|+|y-y_i|\le v_i\}\),从每个点集中随机选择一个点称作\(p_i\)
  • 要求从\(\bigcup_{i=1}^nP_i\)中选出三个不共线的点,使得它们的外接圆期望覆盖的\(p\)点最多,若有多种方案则要求外接圆半径最大。
  • \(n\le10^5,(x_i,y_i)\in S\)且不全部共线

结论题+凸包

仔细想想发现必然存在一种方案能够包含整个\(\bigcup_{i=1}^nP_i\),这时候期望包含\(p\)点数量达到最大。

因此,我们实际上就是要作出一个半径尽可能大的圆,使得这个圆包含所有给定点。

然后就有一个结论:这个圆必然经过凸包上三个相邻顶点。(理性的证明不太会,但感性理解应该还是比较容易的)

所以我们只要求出凸包,之后求每相邻三点的外接圆应该还算是比较简单的,只要求出任意两条中垂线的交点即可。

要求出整张图的凸包,可以先求出每个点集的凸包,只需考虑当点集越界的时候有些特殊,注意讨论即可。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define V 100000
#define DB double
using namespace std;
int n,cnt;struct P
{
	DB x,y;I P(Con DB& a=0,Con DB& b=0):x(a),y(b){}
	I P operator + (Con P& o) Con {return P(x+o.x,y+o.y);}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
	I P operator * (Con DB& o) Con {return P(x*o,y*o);}
	I P operator / (Con DB& o) Con {return P(x/o,y/o);}
	I DB operator * (Con P& o) Con {return x*o.x+y*o.y;}
	I DB operator ^ (Con P& o) Con {return x*o.y-y*o.x;}
	I bool operator < (Con P& o) Con {return x!=o.x?x<o.x:y<o.y;} 
	I bool operator == (Con P& o) Con {return x==o.x&&y==o.y;} 
	I DB L() {return sqrt((*this)*(*this));}
}p[8*N+5];
int T;P s[8*N+5];I void Get()//求凸包
{
	#define pd(A,B,C) (((C-B)^(B-A))>0||(((C-B)^(B-A))==0&&(A<B)==(B<C)))//判断是否需要弹出元素
	RI i;sort(p+1,p+cnt+1),cnt=unique(p+1,p+cnt+1)-p-1;//排序并去重
	for(sort(p+1,p+cnt+1),i=1;i<=cnt;s[++T]=p[i++]) W(T>1&&pd(s[T-1],s[T],p[i])) --T;//从左往右求一遍
	for(i=cnt-1;i;s[++T]=p[i--]) W(T>1&&pd(s[T-1],s[T],p[i])) --T;--T;//从右往左求一遍
}
struct S {P A,B;I S(Con P& a=P(),Con P& b=P()):A(a),B(b){}};
I S MidVer(Con P& A,Con P& B) {P mid=(A+B)/2,t=B-mid;return S(mid,mid+P(t.y,-t.x));}//中垂线
I DB Calc(Con P& A,Con P& B,Con P& C)//计算外接圆半径
{
	S S1=MidVer(A,B),S2=MidVer(A,C);DB w1=(S1.A-S2.A)^(S2.B-S2.A),w2=(S2.B-S2.A)^(S1.B-S2.A);
	P O=S1.A+(S1.B-S1.A)*w1/(w1+w2);return (A-O).L();//求交点即为外心,计算半径
}
int main()
{
	RI i,x,y,z;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d%d",&x,&y,&z),//求出每个点集的凸包,需要讨论
		x+z<=V?p[++cnt]=P(x+z,y):(p[++cnt]=P(V,min(y+z-(V-x),V)),p[++cnt]=P(V,max(y-z+(V-x),0))),
		y+z<=V?p[++cnt]=P(x,y+z):(p[++cnt]=P(min(x+z-(V-y),V),V),p[++cnt]=P(max(x-z+(V-y),0),V)),
		x-z>=0?p[++cnt]=P(x-z,y):(p[++cnt]=P(0,min(y+z-x,V)),p[++cnt]=P(0,max(y-z+x,0))),
		y-z>=0?p[++cnt]=P(x,y-z):(p[++cnt]=P(min(x+z-y,V),0),p[++cnt]=P(max(x-z+y,0),0));
	RI t;DB f,g=0;for(Get(),s[T+1]=s[1],s[T+2]=s[2],i=1;i<=T;++i) (f=Calc(s[i],s[i+1],s[i+2]))>g&&(g=f,t=i);//枚举相邻三个顶点计算半径
	return printf("%.0lf %.0lf\n%.0lf %.0lf\n%.0lf %.0lf\n",s[t].x,s[t].y,s[t+1].x,s[t+1].y,s[t+2].x,s[t+2].y),0;
}
posted @ 2021-04-03 16:46  TheLostWeak  阅读(72)  评论(0编辑  收藏  举报