題目鏈接:https://atcoder.jp/contests/abc157/tasks/abc157_f
題目大意:
有n塊餅,有一個熱源,假設熱源在X,Y,那麼第i塊餅加熱所需要的時間是,問加熱至少k塊餅需要的最少時間。
題目思路:
首先因爲時間是實數域的,實數的答案在我有限的知識水平中,要麼是唯一確切的答案,能夠通過某個公式或者某種遞推得到,要麼就只有二分or三分。這道題顯然是後者,所以現在需要考慮二分哪個量。這個題目涉及的變量很少,能夠二分的只有兩種可能(在我看來只有兩種),要麼是二分座標,要麼是直接二分時間。可以發現二分座標沒有單調性,所以唯一的可能就是二分時間。如何判斷一個時間是否滿足要求?觀察題目要求的式子,如果對於每個餅,用時間除以,就是對於這個餅來說要在規定時間內被加熱,熱源得在這個圓裏面。所以問題就轉換爲了,現在有n個圓,是否存在某個點在k個圓內。這個問題卡了我非常久,我怎麼也想不出怎麼處理實數域上的數量統計。後來看了題解才知道,這個點只用枚舉每個圓的圓心和所有的圓之間的交點即可。這是爲什麼呢?因爲可以發現,對於k個圓的重合部分的那個區域,他的邊一定是由這k個圓中的邊緣組成的,那麼在每個轉換的部分,就是某兩個圓的交點。那麼爲什麼還需要考慮圓心呢?考慮這樣一種情況,有兩個圓相交,他們的相交部分裏面有一個非常小的圓(與這兩個圓都不相交),這樣的話,在那個小圓的內部其實是被三個圓覆蓋的,但是這個小圓的內部不是任何兩個圓的交點。
以下是代碼:
#include<bits/stdc++.h>
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);
}
}