E869120本をSwiftで解く(問題58-90)

# 📚 書籍情報

[問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本](https://direct.gihyo.jp/view/item/000000002544)

# 演習問題集(AtCoder)

[アルゴリズムと数学 演習問題集](https://atcoder.jp/contests/math-and-algorithm)

# 解説(公式)

[E869120/math-algorithm-book/editorial](https://github.com/E869120/math-algorithm-book/tree/main/editorial)

# 解答例

## 058 - Move on Squares 1

参考: [競技プログラミングで解法を思いつくための典型的な考え方](https://algo-logic.info/how-to-think-cp/)

```swift:058 - Move on Squares 1
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, x, y): (Int, Int, Int) = (input[0], input[1], input[2])

// |x| + |y| ≦ n かつ |x + y|とnの偶奇が一致 であれば到達可能
let isReachable: Bool = (abs(x) + abs(y) <= n && abs(x + y) % 2 == n % 2) ? true : false
if (isReachable == true) { print("Yes") }
else { print("No") }
```

## 059 - Power of Two

```swift:059 - Power of Two
let n: Int = Int(readLine()!)!

// 2^nの一の位は{2, 4, 8, 6}の繰り返し
let units: [Int] = [6, 2, 4, 8]
print(units[n % 4])
```

## 060 - Stones Game 1

```swift:060 - Stones Game 1
let n: Int = Int(readLine()!)!

if (n % 4 == 0) {
  print("Second")
}
else {
  print("First")
}
```

## 061 - Stones Game 2

$ n $個の石が残っている状態における現在手番のプレイヤーの勝敗を表にすると、以下のようになる。

| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|  | L | W | L | W | W | W | L | W | W | W | W | W | W | W | L |

上記の結果から、以下の規則性を見出せる。

> $ n = 2^k - 1\ (k \geq 1) $ のときにのみ、現在手番のプレイヤーは負ける

あとは法則が全ての非負整数$ k $において成り立つことを証明すればよい。

$ k = 1 \Leftrightarrow n = 1 $ のとき、現在手番のプレイヤー(以下、$ A $とする)は負ける。

$ k = m \Leftrightarrow n = 2^m - 1\ (m \geq 1) $ のとき、$ A $が負けると仮定する。

ここで、$ k = m+1 \Leftrightarrow n = 2^{m+1} - 1 $ のときに$ A $を勝たせる場合、**次に手番のプレイヤー(以下、$ B $とする)が負ける状態**を作るため$ A $は$ 2^m $個の石を取ればよいが、$ n = 2^{m+1} - 1 $の状態で$ A $が取れる石の個数$ a $は、条件より次のように表される。

```math
1 \leq a \leq \frac{2^{m+1} - 1}{2} = 2^m - \frac{1}{2} \\
\Leftrightarrow
1 \leq a \leq 2^m - 1\ (m \geq 1)
```

よって、$ k = m+1 $ の場合もAは負ける。
上記のことから、数学的帰納法より命題は真である。

```swift:061 - Stones Game 2
let n: Int = Int(readLine()!)!

var (isWinnable, isOne): (Bool, Bool) = (false, true)
// 2進数表記の2^m - 1は2^0の位から順に1が続き、2^kの位で0が初めて登場すると以降の位で1は登場しない
for i in 0 ... 60 {
  if (isOne == true) {
    if (n & (1 << i) == 0) { isOne = false }
  }
  else {
    // 2^m - 1でない場合は先手必勝
    if (n & (1 << i) != 0) { isWinnable = true }
  }
}

if (isWinnable == true) {
  print("First")
}
else {
  print("Second")
}
```

## 062 - Teleporter

```swift:062 - Teleporter
let k: Int = readLine()!.split(separator: " ").map{ Int($0)! }[1]
var destinations: [Int] = [0]
readLine()!.split(separator: " ").forEach{ destinations.append(Int($0)!) }

var visitedCount: [Int] = [Int](repeating: 0, count: destinations.count)

// 最初の街1は到達済とする
visitedCount[1] += 1
var (notLoops, loops): ([Int], [Int]) = ([1] ,[Int]())

var next: Int = destinations[1]
while (true) {
  // 未到達の街であればループ対象外
  if (visitedCount[next] == 0) {
    notLoops.append(next)
    visitedCount[next] += 1
    next = destinations[next]
  }
  // 到達済の街であればループ対象なので、初回のループにのみ注目
  else if (visitedCount[next] == 1) {
    loops.append(next)
    visitedCount[next] += 1
    next = destinations[next]
  }
  // 2回目以降のループは注目しない
  else { break }
}

// kがループになる前の移動回数である場合
if (k < notLoops.count) {
  print(notLoops[k])
}
// kがループになった後の移動回数である場合
else {
  print(loops[(k - notLoops.count) % loops.count])
}
```

## 063 - Move on Squares 2

> $ 2 $ 以上の非負整数 $n$ を用いて表される $ n \times n $ のマスについて、$ n $ が奇数の場合は一筆書きができない

> 証明
奇数回の移動によって到達可能なマスを $ A $、偶数回の移動によって到達可能なマスを $ B $ とすると、出発地点は $ B $ であり、ゴール直前のマスは $ A $ でなければならない。<br>
ここで、一筆書きをする際の直前のマスへの移動には $ n^2 - 1 $ 回の移動が必要であるが、$ n $ が奇数の場合、直前のマスは $ B $ となるため条件に反する。

```swift:063 - Move on Squares 2
let n: Int = Int(readLine()!)!

if (n % 2 == 0) {
  print("Yes")
}
else {
  print("No")
}
```

## 064 - All Zero

```swift:064 - All Zero
var sum: Int = 0
let k: Int = readLine()!.split(separator: " ").map{ Int($0)! }[1]
readLine()!.split(separator: " ").forEach{ sum += Int($0)! }

if (k < sum || k % 2 != sum % 2) {
  print("No")
}
else {
  print("Yes")
}
```

## 065 - Bishop

```swift:065 - Bishop
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (h, w): (Int, Int) = (input[0], input[1])

// 1行または1列の場合は移動不可
if (h == 1 || w == 1) { print(1) }
// 2x2以上のマスの場合
else {
  // 奇数x奇数である場合は移動可能マス数は「マス数の半分 + 1」
  if (h % 2 == 1 && w % 2 == 1) { print((h * w / 2) + 1) }
  // 縦横のどちらかが偶数の場合は移動可能マス数は「マス数の半分」
  else { print(h * w / 2) }
}
```

## 066 - Three Cards

:::note
全探索に時間がかかる場合は、**走査範囲を絞れるだけ絞る**
:::

```swift:066 - Three Cards
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, k): (Int, Int) = (input[0], input[1])

/// 各カードの差の絶対値がk未満(= k - 1以下)になる余事象の場合の数
var complement: Int = 0
/*
 黒・白・灰のカードの値をそれぞれa, b, cとすると、aの値を固定することで
 b, cの走査範囲を以下のように絞ることができる
 max(1, a - (k - 1)) ≦ b, c ≦ min(a + (k - 1), n)
 */
for a in 1 ... n {
  for b in max(1, a - (k - 1)) ... min(a + (k - 1), n) {
    for c in max(1, a - (k - 1)) ... min(a + (k - 1), n) {
      if (abs(b - c) <= k - 1) { complement += 1 }
    }
  }
}

print(n * n * n - complement)
```

## 067 - Cross Sum（★2）

```swift:067 - Cross Sum（★2）
let hw: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (h, w): (Int, Int) = (hw[0], hw[1])
var nums: [[Int]] = [[Int]](repeating: [0], count: h + 1)
for i in 1 ... h {
  nums[i] += readLine()!.split(separator: " ").map{ Int($0)! }
}

var (rowSums, colSums): ([Int], [Int]) = ([0], [0])
var (colSum, answer): (Int, Int)
var output: String = ""
// 行単位での合計
for r in 1 ... h {
  // 行合計
  rowSums.append(nums[r].reduce(0, +))
}

// 列単位での合計
for c in 1 ... w {
  colSum = 0
  nums[1 ... h].forEach{ colSum += $0[c] }
  colSums.append(colSum)
}

for r in 1 ... h {
  output = ""
  for c in 1 ... w {
    answer = rowSums[r] + colSums[c] - nums[r][c]
    output += (c != w) ? "\(answer) " : "\(answer)"
  }
  print(output)
}
```

## 068 - Number of Multiples 2

:::note
集合 $ S_1, S_2, S_3, \cdots , S_n $ の和集合の要素数は、**1つ以上の集合を選ぶ全てのパターン**に対して以下の操作を行った結果となる

**奇数**個選ぶ場合 $ \Leftrightarrow $ 集合群の共通部の要素数を**加算**
**偶数**個(※0を除く)選ぶ場合 $ \Leftrightarrow $ 集合群の共通部の要素数を**減算**
:::

```swift:最小公約数・最小公倍数の取得
/// ユークリッドの互除法を用いて2つの整数の最大公約数を取得する(計算量: O(log *N*))
/// - parameter num1: 最大公約数を求める整数
/// - parameter num2: 最大公約数を求める整数
/// - returns: `num1, num2`の最大公約数
func getGcd(_ num1: Int, _ num2: Int) -> Int {
  var (p, q): (Int, Int) = (num1, num2)
  while (p >= 1 && q >= 1) {
    if (p >= q) { p %= q }
    else { q %= p }
  }
  
  if (p >= q) { return p }
  else { return q }
}

/// ユークリッドの互除法を用いて2つの整数の最小公倍数を取得する(計算量: O(log *N*))
/// - parameter num1: 最小公倍数を求める整数
/// - parameter num2: 最小公倍数を求める整数
/// - returns: `num1, num2`の最小公倍数
func getLcm(_ num1: Int, _ num2: Int) -> Int {
  let gcd: Int = getGcd(num1, num2)
  // 除算を先に行うことでInt型のオーバーフローを避ける
  return num1 / gcd * num2
}
```

```swift:068 - Number of Multiples 2
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, k): (Int, Int) = (input[0], input[1])
let nums: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }

/// `nums`から選択した値の数
var choices: Int
/// `nums`から選択した値の最小公倍数
var lcm: Int

var answer: Int = 0
// ビット全探索(計算量: O(2^k))
// k桁の2進数を用意し、フラグが立っていれば桁に対応するnumsの値を選択することとする
for bit in 1 ..< (1 << k) {
  (choices, lcm) = (0, 1)
  
  for index in 0 ..< k {
    if (bit & (1 << index) != 0) {
      choices += 1
      lcm = getLcm(lcm, nums[index])
    }
  }
  
  // numsから値を奇数個選択した場合は要素数を加算
  if (choices % 2 == 1) {
    answer += n / lcm
  }
  // numsから値を偶数個選択した場合は要素数を減算
  else {
    answer -= n / lcm
  }
}

print(answer)
```

## 069 - Product Max

```swift:069 - Product Max
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (a, b, c, d): (Int, Int, Int, Int) = (input[0], input[1], input[2], input[3])

print(max(a * c, a * d, b * c, b * d))
```

## 070 - Axis-Parallel Rectangle

```swift:070 - Axis-Parallel Rectangle
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, k): (Int, Int) = (input[0], input[1])

var nodes: [[Int]] = [[Int]]()
for _ in 0 ..< n {
  nodes.append(readLine()!.split(separator: " ").map{ Int($0)! })
}

var (count, area, answer): (Int, Int, Int) = (0, Int.max, Int.max)
var (left, right, top, bottom): (Int, Int, Int, Int)
for l in 0 ..< n {
  for r in 0 ..< n {
    for t in 0 ..< n {
      for b in 0 ..< n {
        // x, y座標の範囲を決める
        (left, right, top, bottom) = (
          nodes[l][0],
          nodes[r][0],
          nodes[t][1],
          nodes[b][1]
        )
        
        // 範囲内に含まれる点の個数をカウント
        count = 0
        for i in 0 ..< n {
          if (left <= nodes[i][0] && nodes[i][0] <= right &&
              nodes[i][1] <= top && bottom <= nodes[i][1]) {
            count += 1
          }
        }
        
        // 最小面積を求める
        if (count >= k) {
          area = (right - left) * (top - bottom)
          if (area < answer) { answer = area }
        }
      }
    }
  }
}

print(answer)
```

## 071 - Linear Programming

:::note
`線形計画法(Linear Programming; LP)` … 他の線形不等式(=1次不等式)の制約条件を全て満たす線形関数 $ f(x, y) $ の取りうる最大または最小の値を求める手法
:::

例として、以下の制約を満たす線形関数 $ f(x, y) = x + y $ の最大値について検討する。

- $ 3x + y \leq 10 $
- $ 2x + y \leq 7 $
- $ 3x + 4y \leq 19 $
- $ x + 2y \leq 9 $

線形関数 $ f(x, y) $ の最大値は2直線の交点となるため、2直線

```math
a_{i}x + b_{i}y = c_{i} \Leftrightarrow y = - \frac{a_i}{b_i}x + \frac{c_i}{b_i} \\
a_{j}x + b_{j}y = c_{j} \Leftrightarrow y = - \frac{a_j}{b_j}x + \frac{c_j}{b_j}
```

の交点 $ (x, y) $ を求めればよい。

ここで、$ \frac{a_i}{b_i} = \frac{a_j}{b_j} \Leftrightarrow a_{i}b_{j} = a_{j}b_{i} $ のときは2直線が平行となるため、交点を求める必要はない。

$ a_{i}b_{j} \neq a_{j}b_{i} $ のとき、交点 $ (x, y) $ は以下のように表せる。

```math
(x, y) = (\frac{ c_{i}b_{j} - c_{j}b_{i} }{ a_{i}b_{j} - a_{j}b_{i} }, \frac{ c_{i}a_{j} - c_{j}a_{i} }{ b_{i}a_{j} - b_{j}a_{i} } )
```

```swift:071 - Linear Programming
let n: Int = Int(readLine()!)!
var nums: [[Double]] = [[Double]]()
for _ in 0 ..< n {
  nums.append(readLine()!.split(separator: " ").map{ Double($0)! })
}

var intersection: [Double] = [0, 0]
var answer: Double = Double(Int.min)
var isSatisfied: Bool
for i in 0 ..< n - 1 {
  for j in i + 1 ..< n {
    // 2直線の交点の座標を求める
    (intersection[0], intersection[1]) = (
      (nums[i][2] * nums[j][1] - nums[j][2] * nums[i][1]) / (nums[i][0] * nums[j][1] - nums[j][0] * nums[i][1]),
      (nums[i][2] * nums[j][0] - nums[j][2] * nums[i][0]) / (nums[i][1] * nums[j][0] - nums[j][1] * nums[i][0])
    )
    
    // 全ての条件式を満たすかどうかを判定
    isSatisfied = true
    for k in 0 ..< n where (nums[k][0] * intersection[0] + nums[k][1] * intersection[1] > nums[k][2]) {
      isSatisfied = false
      break
    }
    
    if (isSatisfied == true) { answer = max(answer, intersection[0] + intersection[1]) }
  }
}

print(answer)
```

## 072 - Max GCD 2

```swift:072 - Max GCD 2
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (a, b): (Int, Int) = (input[0], input[1])

// iの倍数がa以上b未満の間に2つ以上存在する場合は最大公約数をiにセット
var answer: Int = 1
for gcd in 1 ..< b where (b / gcd > (a + gcd - 1) / gcd) {
  answer = max(answer, gcd)
}

print(answer)
```

## 073 - Sum of Maximum

```swift:073 - Sum of Maximum
let n: Int = Int(readLine()!)!
let nums: [Int] = [0] + readLine()!.split(separator: " ").map{ Int($0)! }
let div: Int = 1000000007

var answer: Int = 0
// index枚選ぶ場合の数(2^index)
var cases: [Int] = [1]
for i in 1 ... n {
  cases.append(2 * cases[i - 1] % div)
}

for i in 1 ... n {
  // nums[i]が最大値となる場合の数 = nums[i]を選ぶ場合の数(=1) × (i - 1)枚の中から選ぶ場合の数
  answer += nums[i] * cases[i - 1]
  answer %= div
}

print(answer)
```

## 074 - Sum of difference Easy

```swift:074 - Sum of difference Easy
let n: Int = Int(readLine()!)!
let nums: [Int] = [0] + readLine()!.split(separator: " ").map{ Int($0)! }

var (initial, answer): (Int, Int) = (1 - n, 0)
// nums[i]が加算される回数は初項initial, 公差2の等差数列の一般項initial + (i - 1) * 2で表される
for i in 1 ... n {
  answer += nums[i] * (initial + (i - 1) * 2)
}

print(answer)
```

## 075 - Pyramid

```swift:組合せの総数の剰余を取得
/// 繰り返し二乗法を用いて累乗の剰余を取得する(計算量: O(log *exp*))
/// - parameters:
///  - base: `Int`型の底(base)
///  - exp: `Int`型の冪指数(exponent)
///  - div: `Int`型の除数(division)
/// - returns: 累乗`base`^`exp`を除数`div`で割ったときの剰余
func getModPower(_ base: Int, _ exp: Int, _ div: Int) -> Int {
  var (mod, answer): (Int, Int) = (base, 1)
  
  // 冪指数expについて、2^i(0 ≦ i ≦ 30)のフラグが立っている場合は剰余を更新
  // → 10^9 < 2^30であるため、iの最大値を30とする
  for i in 0 ..< 30 {
    if (exp & (1 << i) != 0) {
      answer *= mod
      answer %= div
    }
    mod *= mod
    mod %= div
  }
  
  return answer
}

/// 繰り返し二乗法を用いて組合せの総数の剰余を取得する(計算量: O(log *div*))
/// - parameters:
///  - n: `Int`型の要素数
///  - r: `Int`型の組合せ数
///  - factMods: `n!`を`div`で割った剰余を格納する`[Int]`型配列
///  - div: `Int`型の除数
/// - returns: nCrを`div`で割ったときの剰余
func getCombinationMod2(_ n: Int, _ r: Int, _ factMods: [Int], _ div: Int) -> Int {
  // nCrの分母(=除数)となるr!,(n-r)!のモジュラ逆数を求める(計算量: O(log div))
  let (rFactMMI, nrFactMMI): (Int, Int) = (
    getModPower(factMods[r], div - 2, div) % div,
    getModPower(factMods[n - r], div - 2, div) % div
  )
  
  return factMods[n] * rFactMMI % div * nrFactMMI % div
}
```

```swift:075 - Pyramid
let n: Int = Int(readLine()!)!
let nums: [Int] = [0] + readLine()!.split(separator: " ").map{ Int($0)! }
let div: Int = 1000000007

// 階乗の剰余を取得(計算量: O(N))
var factMods: [Int] = [Int](repeating: 1, count: n + 1)
for i in 1 ... n {
  factMods[i] = (factMods[i - 1] * i) % div
}

// nums[i]は最上段に到達するまでの移動回数n - 1のうち、右方向に(i - 1)回進む場合の数だけ加算
var answer: Int = 0
for i in 1 ... n {
  answer += nums[i] * getCombinationMod2(n - 1, i - 1, factMods, div)
  answer %= div
}

print(answer)
```

## 076 - Sum of difference

```swift:076 - Sum of difference
let n: Int = Int(readLine()!)!
let nums: [Int] = [0] + readLine()!.split(separator: " ").map{ Int($0)! }.sorted()

var (initial, answer): (Int, Int) = (1 - n, 0)
// nums[i]が加算される回数は初項initial, 公差2の等差数列の一般項initial + (i - 1) * 2で表される
for i in 1 ... n {
  answer += nums[i] * (initial + (i - 1) * 2)
}

print(answer)
```

## 077 - Distance Sum

```swift:077 - Distance Sum
let n: Int = Int(readLine()!)!
var (x, y): ([Int], [Int]) = ([Int.min], [Int.min])
var input: [Int]
for _ in 1 ... n {
  input = readLine()!.split(separator: " ").map{ Int($0)! }
  x.append(input[0])
  y.append(input[1])
}
x.sort()
y.sort()

var (initial, answer) = (1 - n, 0)
// nums[i]が加算される回数は初項initial, 公差2の等差数列の一般項initial + (i - 1) * 2で表される
for i in 1 ... n {
  answer += (x[i] + y[i]) * (initial + (i - 1) * 2)
}

print(answer)
```

## 078 - Difference Optimization 1

:::note
`最適化問題(Optimization)` … 全ての制約を満たす値の最大・最小値を求める問題
:::

```swift:幅優先探索を用いた最短経路長の取得
/// 幅優先探索を用いてノード群の最短経路長を取得(計算量: O(*M + N*); *M*はエッジ数、*N*はノード数)
/// - parameter nodes: `[[Int]]`型の隣接リスト表現(最初のインデックスはダミー)
/// - returns: 最短経路長を格納した`[Int]`型配列(最初のインデックスはダミー)
func getShortestDistances(_ nodes: [[Int]], _ start: Int) -> [Int] {
  var distances: [Int] = [Int](repeating: -1, count: nodes.count)
  
  /// 幅優先探索
  /// - parameter start: 始点のノード番号
  func bfs(_ start: Int) -> Void {
    var queue: [Int] = [Int]()
    // 始点の最短経路長を0にセット、キューに追加
    distances[start] = 0
    queue.append(start)
    
    while (queue.isEmpty == false) {
      // キューから先頭要素(ノード番号)を抽出
      let pop: Int = queue.removeFirst()
      
      // 抽出したノードと隣接するノードを走査
      for i in 0 ..< nodes[pop].count {
        let neighbor: Int = nodes[pop][i]
        
        // 隣接ノードが未到達ノードであれば最短経路長を更新、キューに追加
        if (distances[neighbor] == -1) {
          distances[neighbor] = distances[pop] + 1
          queue.append(neighbor)
        }
      }
    }
  }
  
  // ノード1を始点とした幅優先探索
  bfs(start)
  
  return distances
}
```

```swift:078 - Difference Optimization 1
let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, m): (Int, Int) = (input[0], input[1])

// 関係性をグラフとして処理
var nodes: [[Int]] = [[Int]](repeating: [], count: n + 1)
nodes[0] = [0]
var members: [Int]
for _ in 1 ... m {
  members = readLine()!.split(separator: " ").map{ Int($0)! }
  nodes[members[0]].append(members[1])
  nodes[members[1]].append(members[0])
}

// 幅優先探索を用いた最短経路長の取得
let answers: [Int] = getShortestDistances(nodes, 1)

// 最短経路長が120を超える または 存在しない(=-1)場合は最大値(=120)を出力
for answer in answers[1 ... n] {
  if (answer > 120 || answer == -1) {
    print(120)
  }
  else {
    print(answer)
  }
}
```

## 079 - ModSum

```swift:079 - ModSum
let n: Int = Int(readLine()!)!

/*
 N = {1, 2, 3, .. , n - 1, n}に対して、
 P = {1 + 1, 2 + 1, 3 + 1, .. , (n - 1) + 1, 1}と設定した場合が
 N_{i} % P_{i} の総和が最大となる
 → 1からn-1までの総和が最大値
 */
print((n - 1) * n / 2)
```

## 080 - Difference Optimization 2

```swift:Dijkstra法を用いた最短経路長の取得
/// Dijkstra法を用いてノード0を始点とした各ノードへの重み付き有向グラフの最短経路長を取得する(計算量: O((*M + N*) log *N*))
/// - parameter nodes: `[[[Int]]]`型の隣接リスト表現
/// - returns: 各ノードへの最短経路長を格納した`[Int]`型配列
func getShortestPath(_ nodes: [[[Int]]]) -> [Int] {
  /// ノード0からインデックスに対応するノードまでの暫定累計コスト
  var distances: [Int] = [Int](repeating: Int.max, count: nodes.count)
  /// 始点ノードとして設定済かどうか
  var isDetermined: [Bool] = [Bool](repeating: false, count: nodes.count)
  /// 累計コストを優先度とする優先度付きキュー([隣接ノード, 累計コスト])
  var pQueue: PriorityQueue<(Int, Int)> = PriorityQueue<(Int, Int)>{ $0.1 < $1.1 }
  
  // 始点となるノード0を累計コスト0としてキューに追加
  pQueue.append((0, 0))
  
  while (pQueue.isEmpty == false) {
    // ノード0からの累計コストが最小となる始点ノード
    let from: Int = pQueue.popFirst()!.0
    
    // すでに始点ノードとして走査済の場合はスキップ
    if (isDetermined[from] == true) { continue }
    
    // 始点ノードまでの累計コスト(始点ノードが0の場合は0とする)
    let distanceToFrom: Int
    if (from == 0) { distanceToFrom = 0 }
    else { distanceToFrom = distances[from] }
    
    // 最短距離が確定していない隣接ノードを走査
    for node in nodes[from] where (isDetermined[node[0]] == false) {
      let (to, distance): (Int, Int) = (node[0], distanceToFrom + node[1])
      
      // 暫定累計コストの更新
      distances[to] = min(distances[to], distance)
      // 累計コストが小さい順にソートしながらキューに挿入
      pQueue.append((to, distances[to]))
    }
    
    // 始点ノードを探索済にする
    isDetermined[from] = true
  }
  
  return distances
}
```

```swift:080 - Difference Optimization 2
let input: [Int] = readLine()!.split(separator: " ").map{ Int(String($0))! }
let (n, m): (Int, Int) = (input[0], input[1])

// nodes[i]はノードiの[隣接ノード - 1, 移動コスト]を格納する
var nodes: [[[Int]]] = [[[Int]]](repeating: [[Int]](), count: n)
for _ in 0 ..< m {
  let input2: [Int] = readLine()!.split(separator: " ").map{ Int(String($0))! }
  nodes[input2[0] - 1].append([input2[1] - 1, input2[2]])
  nodes[input2[1] - 1].append([input2[0] - 1, input2[2]])
}

// Dijkstra法を用いて各ノードへの最短経路を取得する
let shortestPath: [Int] = getShortestPath(nodes)

// ノードnまでの最短経路長が初期値Int.maxである場合は-1を出力
if (shortestPath[n - 1] == Int.max) {
  print(-1)
}
else {
  print(shortestPath[n - 1])
}
```

## 081 - Bill Changing Problem

:::note
`貪欲法(Greedy Algorithm)` … **局所的に**最適解を選択することで解を求める手法
:::

参考: [硬貨の問題が貪欲法で解けるための条件](https://qiita.com/s417-lama/items/0cdd95fddb2067876896)

小さい順にソートした数列 $ \lbrace a_1, a_2, \dots , a_n \rbrace $ について、$ a_i 
(1 \leq i \leq n-1) $ に着目する。
非負整数 $ k $ を用いて $ ka_i \geq a_{i+1} $ となる最小の整数を $ p $ 、その差を $ \delta (= pa_i - a_{i+1}) $ とすると、貪欲法を用いた場合の解 $ H(n) $ について常に以下の不等式が成り立てば貪欲法が利用できる。
$$ H(\delta) \leq p - 1 $$

```swift:081 - Bill Changing Problem
let n: Int = Int(readLine()!)!
print((n / 10000) + (n % 10000 / 5000) + (n % 10000 % 5000 / 1000))
```

## 082 - Interval Scheduling Problem

```swift:082 - Interval Scheduling Problem
let n: Int = Int(readLine()!)!

var intervals: [[Int]] = [[Int.min, Int.min]]
for _ in 1 ... n {
  intervals.append(readLine()!.split(separator: " ").map { Int(String($0))! })
}
// 終了時刻の早い順にソート
intervals.sort{ $0[1] < $1[1] }

var (now, answer): (Int, Int) = (intervals[0][1], 0)
for i in 1 ... n {
  // 現在の時刻が次の映画の開始時刻より早ければ次の映画は見れる
  if (now <= intervals[i][0]) {
    // 現在時刻を次の映画の終了時刻にセット
    now = intervals[i][1]
    answer += 1
  }
}

print(answer)
```

## 083 - We Used to Sing a Song Together（★3）

```swift:083 - We Used to Sing a Song Together（★3）
let n: Int = Int(readLine()!)!
let kids: [Int] = readLine()!.split(separator: " ").map{ Int(String($0))! }.sorted()
let schools: [Int] = readLine()!.split(separator: " ").map{ Int(String($0))! }.sorted()

var answer: Int = 0
for i in 0 ..< n {
  answer += abs(kids[i] - schools[i])
}
print(answer)
```

## 084 - Sqrt Inequality

```swift:084 - Sqrt Inequality
/*
 √a + √b < √c の両辺を2乗すると
 2√(ab) < c - a - b
 c - a - b ≦ 0 ⇔ 条件を満たさない
 c - a - b > 0 のとき、両辺を2乗すると
 4 * ab < (c - a - b)^2
 */
let input: [Int] = readLine()!.split(separator: " ").map { Int($0)! }
let (a, b, c): (Int, Int, Int) = (input[0], input[1], input[2])
let d: Int = c - a - b

if (d > 0 && 4 * a * b < d * d) { print("Yes") }
else { print("No") }
```

## 085 - Two Conditions

```swift:085 - Two Conditions
import Foundation

let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, x, y): (Int, Int, Int) = (input[0], input[1], input[2])
for a in 1 ... n {
  for b in a ... n {
    for c in b ... n {
      for d in c ... n {
        let sum: Int = a + b + c + d
        let product: Int = a * b * c * d
        if (sum == x && product == y) {
          print("Yes")
          exit(0)
        }
      }
    }
  }
}
print("No")
```

## 086 - Parentheses Check

```swift:086 - Parentheses Check
import Foundation

let n: Int = Int(readLine()!)!
let s: [String] = Array(readLine()!).map{ String($0) }

var lp: Int = 0
for i in 0 ..< n {
  if (lp >= 0) {
    lp += (s[i] == "(") ? 1 : -1
  }
  else {
    print("No")
    exit(0)
  }
}

print(lp == 0 ? "Yes" : "No")
```

## 087 - Simple Math Easy

```swift:087 - Simple Math Easy
let n: Int = Int(readLine()!)!
let div: Int = 1000000007

let sum1ToN: Int = n * (n + 1) / 2 % div
print(sum1ToN * sum1ToN % div)
```

## 088 - Simple Math

```swift:088 - Simple Math
/// 1から`n`までの総和を`div`で割った剰余を取得する
/// - parameters:
///  - n: `Int`型の最大値
///  - div: `Int`型の除数
/// - returns: `1 ... n`の総和を`div`で割った剰余
func sumToNum(_ n: Int, _ div:Int = 998244353) -> Int {
  return n * (n + 1) / 2 % div
}

let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (a, b, c): (Int, Int, Int) = (input[0], input[1], input[2])
let div: Int = 998244353

let sumA: Int = sumToNum(a)
let sumB: Int = sumToNum(b)
let sumC: Int = sumToNum(c)

print(sumA * sumB % div * sumC % div)
```

## 089 - Log Inequality 2

```swift:089 - Log Inequality 2
import Foundation

let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (a, b, c): (Int, Int, Int) = (input[0], input[1], input[2])

// a ≧ 1の制約上、a ≧ 1^bとなる
if (c == 1) {
  print("No")
  exit(0)
}

/*
 log_2{a} < b * log_2{c}
 ⇔ a < c^b
 */
var fact: Int = 1
// ここで、c ≧ 2であることが保証されているが、2^60 > 10^18よりループ回数は最大でも60回程度に収まる
for _ in 1 ... b {
  // オーバーフローを避けるためa < fact * cを変形
  if (a / c < fact) {
    print("Yes")
    exit(0)
  }
  fact *= c
}
print("No")
```

## 090 - Digit Product Equation（★7）

```swift:090 - Digit Product Equation（★7）
/// 各桁の値の積を求める
/// - parameter n: `Int`型の整数値
/// - returns: `n`の各桁の値の積
func productEachDigit(_ n: Int) -> Int {
  let digits: [Int] = Array(String(n)).map{ String($0) }.map{ Int($0)! }
  return digits.reduce(1, *)
}

/// `f(x)`の候補を取得
/// - parameters:
///  - digit: `num`の桁数
///  - num: `元の数`
func enumerateFx(_ digit: Int, _ num: Int) -> Void {
  // numが11桁に達した場合
  // → numをnumsに追加し、再帰処理を終了する
  if (digit == 11) {
    nums.insert(productEachDigit(num))
    return
  }
  
  // numが11桁に達していない場合
  // → 現在の1の位の値以上の値を1の位に追加
  let min: Int = num % 10
  for i in min ... 9 {
    enumerateFx(digit + 1, num * 10 + i)
  }
}

let input: [Int] = readLine()!.split(separator: " ").map{ Int($0)! }
let (n, b): (Int, Int) = (input[0], input[1])

/// `f(x)`として考えられる値
var nums: Set<Int> = Set<Int>()

// f(x)として考えられる値を列挙
enumerateFx(0, 0)

var answer: Int = 0
for num in nums {
  // f(x)として考えられる値をもとにmを算出(m ≧ 1がここで保証される)
  let m: Int = num + b
  // mをもとにf(m)を算出
  let prodM: Int = productEachDigit(m)
  if (m - prodM == b && m <= n) {
    answer += 1
  }
}

print(answer)
```