AtCoder Beginner Contest 157 F - Yakiniku Optimization Problem(思维+计算几何)

题目链接:https://atcoder.jp/contests/abc157/tasks/abc157_f

 

题目大意:

  有n块饼,有一个热源,假设热源在X,Y,那么第i块饼加热所需要的时间是 c i ∗ ( X − x i ) 2 + ( Y − y i ) 2 c_i*\sqrt{(X-x_i)^2+(Y-y_i)^2} ci(Xxi)2+(Yyi)2 ,问加热至少k块饼需要的最少时间。

 

题目思路:

  首先因为时间是实数域的,实数的答案在我有限的知识水平中,要么是唯一确切的答案,能够通过某个公式或者某种递推得到,要么就只有二分or三分。这道题显然是后者,所以现在需要考虑二分哪个量。这个题目涉及的变量很少,能够二分的只有两种可能(在我看来只有两种),要么是二分坐标,要么是直接二分时间。可以发现二分坐标没有单调性,所以唯一的可能就是二分时间。如何判断一个时间是否满足要求?观察题目要求的式子,如果对于每个饼,用时间除以 c i c_i ci,就是对于这个饼来说要在规定时间内被加热,热源得在这个圆里面。所以问题就转换为了,现在有n个圆,是否存在某个点在k个圆内。这个问题卡了我非常久,我怎么也想不出怎么处理实数域上的数量统计。后来看了题解才知道,这个点只用枚举每个圆的圆心和所有的圆之间的交点即可。这是为什么呢?因为可以发现,对于k个圆的重合部分的那个区域,他的边一定是由这k个圆中的边缘组成的,那么在每个转换的部分,就是某两个圆的交点。那么为什么还需要考虑圆心呢?考虑这样一种情况,有两个圆相交,他们的相交部分里面有一个非常小的圆(与这两个圆都不相交),这样的话,在那个小圆的内部其实是被三个圆覆盖的,但是这个小圆的内部不是任何两个圆的交点。

 

以下是代码:

#include
using namespace std;

#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 5e5+5;
const int MOD= 998244353;
int n,k;
struct node{
    double x,y,c,r;
}a[MAXN];
vector<pair<double,double> >v;
vector<pair<double,double> > cal(double x1,double y1,double r1,double x2,double y2,double r2) {//两圆交点
    x1-=x2,y1-=y2;
    double S=x1*x1+y1*y1,a=(S+r2*r2-r1*r1)/2,D=S*r2*r2-a*a;
    if(D<0) return {};
    double A1=a*x1,B1=y1*sqrt(D);
    double A2=a*y1,B2=x1*sqrt(D);
    return {{(A1+B1)/S+x2,(A2-B2)/S+y2},{(A1-B1)/S+x2,(A2+B2)/S+y2}};
}
bool check(double t){
    rep(i,1,n)a[i].r=t/a[i].c;
    v.clear();
    rep(i,1,n){
        v.push_back(make_pair(a[i].x,a[i].y));
        rep(j,i+1,n){
            auto p=cal(a[i].x,a[i].y,a[i].r,a[j].x,a[j].y,a[j].r);
            for(auto it:p){
                v.push_back(it);
            }
        }
    }
    int len=v.size();
    rep(i,0,len-1){
        pair<double,double>p=v[i];
        int num=0;
        rep(i,1,n){
            if((a[i].x-p.first)*(a[i].x-p.first)+(a[i].y-p.second)*(a[i].y-p.second)<=a[i].r*a[i].r||
               fabs((a[i].x-p.first)*(a[i].x-p.first)+(a[i].y-p.second)*(a[i].y-p.second)-a[i].r*a[i].r)<=1e-8)num++;
            if(num==k)return 1;
        }
    }return 0;
}
int main(){
    while(cin>>n>>k){
        rep(i,1,n)cin>>a[i].x>>a[i].y>>a[i].c;
        double l=0,r=1e9;
        rep(i,1,100){
            double mid=(l+r)/2;
            if(check(mid))r=mid;
            else l=mid;
        }
        printf("%.10lf\n",r);
    }
}

你可能感兴趣的:(计算几何)