L. Machining Disc Rotors 2018-2019 ACM-ICPC, Asia Shenyang Regional Contest

版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_38449464/article/details/89225974