Problem:
Score : 600 points

Problem Statement

Given an undirected tree, let the distance between vertices u and v be the
number of edges on the simple path from u to v.
The diameter of a tree is the maximum among the distances between any two vertices.
We will call a tree good if and only if its diameter is at most K.

You are given an undirected tree with N vertices numbered 1 through N.
For each i (1≦i≦N-1), there is an edge connecting vertices A_i and B_i.

You want to remove zero or more vertices from the tree, so that the resulting tree is good.
When a vertex is removed, all incident edges will also be removed.
The resulting graph must be connected.

Find the minimum number of vertices that you need to remove in order to produce a good tree.

Constraints

2≦N≦2000

1≦K≦N-1

1≦A_i≦N, 1≦B_i≦N

The graph defined by A_i and B_i is a tree.

Input

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

N K
A_1 B_1
A_2 B_2
:
A_{N-1} B_{N-1}

Output

Print the minimum number of vertices that you need to remove in order to produce a good tree.

Sample Input 1

6 2
1 2
3 2
4 2
1 6
5 6

Sample Output 1

2

The tree is shown below. Removing vertices 5 and 6 will result in a good tree with the diameter of 2.

Sample Input 2

6 5
1 2
3 2
4 2
1 6
5 6

Sample Output 2

0

Since the given tree is already good, you do not need to remove any vertex.

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

using System;
using System.Linq;//リストの使用
using System.Collections.Generic;
class Program
{
static int[] numsa = Array.ConvertAll(Console.ReadLine().Split(' '),int.Parse);
static int n = numsa[0];
static int k = numsa[1];
static List<int[]>[] nextList = new List<int[]>[n];//頂点番号ごとの隣接リスト。距離、隣接頂点番号
static int[] colors = new int[n];//最短距離未確定の頂点が0、確定で2
static int[] distances = new int[n];//各頂点への最短距離
static int[] distanceMemos = new int[2];//キューから捨てた最短距離と頂点番号
static int[] diameters = new int[n];
static int[,] nextmemos = new int[n-1,2];
static int answer = int.MaxValue;

	static void Main()
	{
    for(int i = 0; i < n; i++)nextList[i] = new List<int[]>();//リストの宣言が必要
    for(int i = 0; i < n-1; i++)
    {
      int[] nums = Array.ConvertAll(Console.ReadLine().Split(' '),int.Parse);

        nextmemos[i,0] = nums[0]-1;
        nextmemos[i,1] = nums[1]-1;

        int[] nextMemo = new int[2];
        nextMemo[1] = nums[1]-1;//隣接頂点番号
        nextMemo[0] = 1;//距離
        nextList[nums[0]-1].Add(nextMemo);//隣接リストの作成
        
        int[] nextMemoSub = new int[2];
        nextMemoSub[1] = nums[0]-1;//隣接頂点番号
        nextMemoSub[0] = 1;//距離
        nextList[nums[1]-1].Add(nextMemoSub);//隣接リストの作成
    }
    for(int i = 0; i < n; i++)
    {
      if(k % 2 == 0)//距離が偶数
      {
        Dijkstra(i);
        int memo = 0;
        for(int j = 0; j < n; j++)
        {
          if(distances[j] > k/2) memo++;
        }
        //Console.WriteLine(memo);
        answer = Math.Min(answer, memo);//取り除く個数の最小値の更新
      }
    }

    if(k % 2 == 1)//距離が奇数
    {
      int[,] distanceMemosa = new int[n,n];//開始点と各頂点
      for(int i = 0; i < n; i++)
      {
        Dijkstra(i);
        for(int j = 0; j < n; j++) distanceMemosa[i,j] = distances[j];
      }
      for(int i = 0; i < n-1; i++)
      {
        int memo = 0;
        for(int j = 0; j < n; j++)
        {
          if(Math.Min(distanceMemosa[nextmemos[i,0],j], distanceMemosa[nextmemos[i,1],j]) > k/2) memo++;
        }
        //Console.WriteLine(memo+" "+nextmemos[i,0]+" "+nextmemos[i,1]);
        answer = Math.Min(answer, memo);//取り除く個数の最小値の更新
      }
    }

    Console.WriteLine(answer);
	}

  static void Dijkstra(int startVertex)//O((|頂点数|+|辺数|)log頂点数)で各頂点への最短距離を求める
  {
    List<int[]> distanceList = new List<int[]>();//優先度付きキュー
    for(int i = 0; i < n; i++)
    {
      distances[i] = int.MaxValue;//最短距離を無限とおく
      colors[i] = 0;
    }

    int[] newDistance = new int[2];

    //スタート地点を決める
    newDistance[1] = startVertex;//スタート地点番号
    distances[startVertex] = 0;
    colors[startVertex] = 1;

    newDistance[0] = 0;//スタートからスタートまでの距離0
    distanceList.Add(newDistance);

    while(distanceList.Count() != 0)
    {
      distanceList = PopHeap(distanceList);
      int minVertex = distanceMemos[1];//最短頂点の番号
      colors[minVertex] = 2;
      if(distances[minVertex] < distanceMemos[0]) continue;//最小値が最短でないなら無視

      for(int i = 0; i < nextList[minVertex].Count(); i++)
      {
        int v = 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];
          int[] newDistanceSub = new int[2];
          newDistanceSub[0] = distances[v];
          newDistanceSub[1] = v;
          distanceList = PushHeap(distanceList, newDistanceSub);
          colors[v] = 1;
        }
      }
    }
    
  }

  //引数は、昇順にソート済みのリスト
  static List<int[]> PushHeap(List<int[]> nums, int[] 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])//親のほうが大きいなら入れ替え。
      {
        int tmp = nums[n][0];
        int 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<int[]> PopHeap(List<int[]> 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])//子が親より小さいなら入れ替え
      {
        int tmp = nums[j][0];
        int 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;
  }
  
}