Problem:
Score : 600 points

Problem Statement

On the xy-plane, Snuke is going to travel from the point (x_s, y_s) to the point (x_t, y_t).
He can move in arbitrary directions with speed 1.
Here, we will consider him as a point without size.

There are N circular barriers deployed on the plane.
The center and the radius of the i-th barrier are (x_i, y_i) and r_i, respectively.
The barriers may overlap or contain each other.

A point on the plane is exposed to cosmic rays if the point is not within any of the barriers.

Snuke wants to avoid exposure to cosmic rays as much as possible during the travel.
Find the minimum possible duration of time he is exposed to cosmic rays during the travel.

Constraints

All input values are integers.

-10^9 ≤ x_s, y_s, x_t, y_t ≤ 10^9

(x_s, y_s) ≠ (x_t, y_t)

1≤N≤1,000

-10^9 ≤ x_i, y_i ≤ 10^9

1 ≤ r_i ≤ 10^9

Input

The input is given from Standard Input in the following format:

x_s y_s x_t y_t
N
x_1 y_1 r_1
x_2 y_2 r_2
:
x_N y_N r_N

Output

Print the minimum possible duration of time Snuke is exposed to cosmic rays during the travel.
The output is considered correct if the absolute or relative error is at most 10^{-9}.

Sample Input 1

-2 -2 2 2
1
0 0 1

Sample Output 1

3.6568542495

An optimal route is as follows:

Sample Input 2

-2 0 2 0
2
-1 0 2
1 0 2

Sample Output 2

0.0000000000

An optimal route is as follows:

Sample Input 3

4 -2 -2 4
3
0 0 2
4 0 1
0 4 1

Sample Output 3

4.0000000000

An optimal route is as follows:

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Linq;//リストの使用
using System.Collections.Generic;
using System.Text;//テキストの高速出力に必要
class Program
{
static double[] distanceMemos = new double[2];//キューから捨てた最短距離と頂点番号

  static void Main()
  {
    string[] input = Console.ReadLine().Split(' ');
    double xs = double.Parse(input[0]);
    double ys = double.Parse(input[1]);
    double xt = double.Parse(input[2]);
    double yt = double.Parse(input[3]);
    int n = int.Parse(Console.ReadLine());
    double[,] matrix = new double[n+2,3];//行列
    for(int i = 0; i < n; i++)
    {
    	double[] numsa = Array.ConvertAll(Console.ReadLine().Split(' '),double.Parse);
    	for(int j = 0; j < 3; j++) matrix[i,j] = numsa[j];
    }
    matrix[n,0] = xs;
    matrix[n,1] = ys;
    matrix[n,2] = 0;
    matrix[n+1,0] = xt;
    matrix[n+1,1] = yt;
    matrix[n+1,2] = 0;
    
    double[,] edges = new double[(n+2)*(n+1), 3];
    int edgeCount = 0;

    for(long i = 0; i < n+2; i++)
    {
      for(long j = i+1; j < n+2; j++)
      {
        edges[edgeCount,0] = i;
        edges[edgeCount,1] = j;
        edges[edgeCount,2] = Math.Sqrt((matrix[i,0]-matrix[j,0])*(matrix[i,0]-matrix[j,0])
          + (matrix[i,1]-matrix[j,1])*(matrix[i,1]-matrix[j,1]))
            - matrix[i,2] - matrix[j,2];
        if(edges[edgeCount,2] < 0) edges[edgeCount,2] = 0;
        edgeCount++;

        edges[edgeCount,0] = j;
        edges[edgeCount,1] = i;
        edges[edgeCount,2] = edges[edgeCount-1,2];
        //Console.WriteLine(i+" "+j+" "+edges[edgeCount,2]);
        edgeCount++;
      }
    }

    //for(int i = 0; i < n+2; i++) Console.WriteLine(i+" "+Dijkstra(n+2,(n+2)*(n+1)/2,n,edges)[i]);

    Console.WriteLine(Dijkstra(n+2,(n+2)*(n+1),n,edges)[n+1]);
  }
 
  static double[] Dijkstra(int vertexCount, int edgeCount, int startV, double[,] edges)
  {//ダイクストラ法により、O((|頂点数|+|辺数|)log頂点数)で各頂点への最短距離を求める
  //引数は頂点数、辺数、開始頂点(0-index)、辺の始点と終点と距離
  //返り値は各頂点までの最短距離、ただし到達できない場合は-1

    int n = vertexCount;
    List<double[]>[] nextList = new List<double[]>[n];//頂点番号ごとの隣接リスト。距離、隣接頂点番号
    for(long i = 0; i < n; i++)
    {
      nextList[i] = new List<double[]>();//リストの宣言が必要
    }
    long[] colors = new long[n];//最短距離未確定の頂点が0、確定で2
    double[] distances = new double[n];//各頂点への最短距離

    for(long i = 0; i < edgeCount; i++)
    {
      double[] nextMemo = new double[2];
      nextMemo[1] = edges[i,1];//隣接頂点番号
      nextMemo[0] = edges[i,2];//距離
      nextList[(int)edges[i,0]].Add(nextMemo);//隣接リストの作成
    }
  
    List<double[]> distanceList = new List<double[]>();//優先度付きキュー
    for(long i = 0; i < n; i++)
    {
      distances[i] = double.MaxValue;//最短距離を無限とおく
    }
 
    double[] newDistance = new double[2];
 
    //スタート地点を決める
    newDistance[1] = startV;//スタート地点番号
    distances[startV] = 0;
    colors[startV] = 1;
 
    newDistance[0] = 0;//スタートからスタートまでの距離0
    distanceList.Add(newDistance);
 
    while(distanceList.Count() != 0)
    {
      distanceList = PopHeap(distanceList);
      long minVertex = (long)distanceMemos[1];//最短頂点の番号
      colors[minVertex] = 2;
      if(distances[minVertex] < distanceMemos[0]) continue;//最小値が最短でないなら無視
 
      for(long i = 0; i < nextList[minVertex].Count(); i++)
      {
        long v = (long)nextList[minVertex][(int)i][1];
        if(colors[v] == 2) continue;
        if(distances[v] > distances[minVertex] + nextList[minVertex][(int)i][0])
        {
          distances[v] = distances[minVertex] + nextList[minVertex][(int)i][0];
          double[] newDistanceSub = new double[2];
          newDistanceSub[0] = distances[v];
          newDistanceSub[1] = v;
          distanceList = PushHeap(distanceList, newDistanceSub);
          colors[v] = 1;
        }
      }
    }

    for(long i = 0; i < n; i++)
    {
      if(distances[i] >= double.MaxValue-1) distances[i] = -1;
        //全頂点について最短距離の出力。到達不可能な頂点は-1を出力。
    }
    return distances;
  }
 
  //引数は、昇順にソート済みのリスト
  static List<double[]> PushHeap(List<double[]> nums, double[] addNums)//現在のヒープnumsにaddNumを追加
  {
    int n = nums.Count;
    nums.Add(addNums);
 
    while (n > 0)
    {
      int i = (n-1)/2;
      if (nums[n][0] < nums[i][0])//親のほうが大きいなら入れ替え。
      {
        double tmp = nums[n][0];
        double tmpSub = nums[n][1];
        nums[n][0] = nums[i][0];
        nums[n][1] = nums[i][1];
        nums[i][0] = tmp;
        nums[i][1] = tmpSub;
      }
      n = i;
    }
    return nums;
  }
 
  static List<double[]> PopHeap(List<double[]> nums)//ヒープnumsからポップ
  {
    int n = nums.Count - 1;
    distanceMemos[0] = nums[0][0];//捨てる値の保存
    distanceMemos[1] = nums[0][1];
    nums[0][0] = nums[n][0];//最小値である根を削除してリストの最後を持ってくる
    nums[0][1] = nums[n][1];
    nums.RemoveAt(n);
 
    for (int i = 0, j; (j = 2*i+1) < n; )
    {
      if ((j != n-1) && (nums[j][0] > nums[j+1][0]))// ２つの子のうち、値の小さい方を選ぶ
        j++;
      if (nums[i][0] > nums[j][0])//子が親より小さいなら入れ替え
      {
        double tmp = nums[j][0];
        double tmpSub = nums[j][1];
        nums[j][0] = nums[i][0];
        nums[j][1] = nums[i][1];
        nums[i][0] = tmp;
        nums[i][1] = tmpSub;
      }
      i = j;
    }
    return nums;
  }
   
}