Problem:
問題 F ボ〜ル

問題文

2 次元平面上の原点にボールがあり，x 軸の正方向との角度が反時計回りに見て 0 度 180 度までの間の方向に一様な確率で発射されようとしている (発射される方向は整数角であるとは限らない)．ボールの大きさは十分小さく，平面上では点であると見なすことにする．この問題における目的は，このボールをできるだけ高い確率で捕獲することである．

平面上に N 個の場所 (xi, yi) が与えられる．ボールを捕獲するために，あなたは N 個の場所から K 個の場所を選んで，それぞれの場所に人を配置することができる．人は i 番目の与えられた場所に対して半径 ri 以内の距離まで動いてボールを取ることが出来る．

人の配置をうまく選んでボールを捕獲できる確率を最大にするとき，その確率を出力せよ．

入力形式

入力は以下の形式で与えられる．

N K
x1 y1 r1
...
xN yN rN

N はボールを捕獲するために人を置くための場所の数であり，K はその中から使うことの出来る場所の数である．
(xi, yi) は i 番目の場所の座標であり，ri はそこから動くことの出来る距離である．

出力形式

確率を小数表記で 1 行に出力せよ．小数点以下何桁でも出力して構わないが，相対誤差あるいは絶対誤差が 10-6 未満になっていなければならない．

制約

1≤ N≤ 1,500, 1≤ K≤ N

|xi| ≤ 1,000, |yi| ≤ 1,000, 1 ≤ ri < (xi2 + yi2)1/2

入力値はすべて整数である．

入出力例

入力例 1

2 1
10 10 10
-10 10 10

出力例 1

0.50

2 つ場所があり，そのうちのどちらかに人を配置できる．この場合，どちらに配置しても確率は 1/2 になる．

入力例 2

2 2
10 10 10
-10 10 10

出力例 2

1.0

入力例 3

5 3
-10 -10 5
10 10 2
-10 10 3
-10 0 4
10 0 2

出力例 3

0.3574057314330

入力例 4

4 2
1 1 1
2 2 2
3 3 3
4 4 4

出力例 4

0.50

Solution in C++ (judge verdict: Accepted)

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <cstring>
#include <cassert>

using namespace std;

#define FOR(i,k,n) for(int i=(k); i<(int)n; ++i)
#define REP(i,n) FOR(i,0,n)

const int INF = 100000000;
const double EPS = 1e-8;
typedef pair<double, double> P;

double dp[1510][1510];
int main(){
  int N, K;
  cin>>N>>K;
  vector<P> _v, v;
  REP(i, N){
    double x, y, r;
    cin>>x>>y>>r;
    double t1 = atan2(y, x);
    double t2 = asin(r/sqrt(x*x+y*y));
    double b = t1 - t2, e = t1 + t2;
    if(b < -M_PI && e < 0) { b += 2*M_PI; e += 2*M_PI; }
    if(b < 0) b = 0;
    if(e > M_PI) e = M_PI;
    if(b < e) _v.push_back(P(b,e));
  }
  sort(_v.begin(), _v.end());
  //REP(i, _v.size()) printf("(%lf,%lf) ", _v[i].first, _v[i].second);
  REP(i, _v.size()){
    while(i + 1 < _v.size() && abs(_v[i].first - _v[i+1].first) < EPS) i++;
    v.push_back(_v[i]);
  }
  int M = v.size();
  vector<int> from(M, INF);
  REP(i, M) FOR(j, i+1, M) if(v[i].second > v[j].first) from[j] = min(from[j], i);
  memset(dp, 0, sizeof(dp));
  dp[1][1] = v[0].second - v[0].first;
  FOR(i, 1, M){
    int f = from[i];
    if(f == INF) f = i - 1;
    FOR(k, 1, i + 2) dp[i+1][k] = max(dp[i][k], max(
          dp[f+1][k-1] + (v[i].second - max(v[f].second, v[i].first)),
          dp[f][k-1] + (v[i].second - v[i].first)));
    //printf("%d:",f); REP(k, i + 2) printf("%lf ", dp[i+1][k]); cout<<endl;
  }
  double ans = 0;
  REP(k, K + 1) ans = max(ans, dp[M][k]/M_PI);
  printf("%.13lf\n",ans);
  return 0;
}