版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38449464/article/details/89225974
1、Problem:
https://codeforces.com/gym/101955/problem/L
2、tags:
几何 思维 细节处理
3、Mean:
已知原点有半径为R的圆,求其被n个输入圆切割后,剩下部分的最大直径.
最大直径即所有剩余点两两间最大距离
4、Solution:
大胆假设答案就是在圆边缘取到
由圆心和半径可以方便算出角度,所以可以把圆分为[0,2pi]
每次切割时,先滤掉不相交的情况
然后算出被切割的角度,放入无效边的集合s.(要保证角度在[0,2pi]内,对于2pi跨到0的也是一样. 因为正常是r>l,横跨了以后是l>r,所以分类处理即可)
把无效边排序,遍历一次,取出有效边放入集合v. 有效边即两个无效边中夹着的那块
然后n2遍历有效边. 对于所有端点两两比较取max;再对端点的对称点,检查是否在有效边范围内,若在则ans为直径.
5、Mistakes:
det 余弦定理算错
开始是直接处理有效边,这样需要每次找到删除再放两个新的.复杂度多一个log,大概乘*10吧,T的妥妥的.
6、Gains:
跨越的范围怎么处理?因为l,r大小关系不一样,分类讨论即可.
给定一点怎么算角度?用cos,tan还要特判垂直太麻烦.
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=110;
const double pi=acos(-1);
const double pi2=2.0*pi;
double ans,R;
int n;
double x[N],y[N],r[N];
vector<pair<double,double>> s,v;//s->被割端点集合 v->有效端点集合
int seg(double x,double y,double a){
if(x<y && x<a && a<y) return 1;//正常情况下
if(x>y && (x<a||a<y)) return 1;//跨过2pi-0,则满足一半即可
return 0;
}
void cal(double a,double b){
double d=fabs(a-b);
if(d>pi) d=pi2-d;//计算点之间的弧度
ans=max(ans,R*sin(d*0.5)*2.0);
}
void work(){//max->端与端最大距离or端对称点存在
for(int i=0;i<v.size();i++){
double l=v[i].first,r=v[i].second;
double a=(l>pi)?l-pi:l+pi;//对称点
double b=(r>pi)?r-pi:r+pi;
int f=0;
for(int j=0;j<v.size();j++){
double L=v[j].first,R=v[j].second;
f=max(f,seg(L,R,a)); f=max(f,seg(L,R,b));//判断对称点
cal(l,L); cal(r,L);
cal(l,R); cal(r,R);
}
if(f){ ans=2*R; break;}
}
}
int main(){
int T;
cin>>T;
for(int _=1;_<=T;_++){
ans=0;
s.clear();
v.clear();
cin>>n>>R;
for(int i=1;i<=n;i++){
scanf("%lf %lf %lf",&x[i],&y[i],&r[i]);
double d=sqrt(x[i]*x[i]+y[i]*y[i]);
if(R+r[i]<d || r[i]+d<R) continue;//不切圆边,跳过
double m=acos(x[i]/d);//中心位置
if(y[i]<0) m=pi2-m;//cos在三四象限,修正值
double det=acos((R*R+d*d-r[i]*r[i])/(2.0*R*d));//偏移量
double l=m-det,r=m+det;//左右
if(l<0) l+=pi2;//角度范围[0,2pi]
if(r>pi2) r-=pi2;//修正值
s.push_back({l,r});
}
sort(s.begin(),s.end());//按l排序
s.push_back(s[0]);//有效点取无效点中间. 0和end()特殊处理
for(int i=1;i<s.size();i++)
v.push_back({s[i-1].second,s[i].first});//cout<<s[i].first<<endl;
work();
if(s.empty()) ans=2*R;//如果没有被割
printf("Case #%d: %.15lf\n",_,ans);
}
}