---
# System prepended metadata

title: AtCoder Beginner Contest 170 題解
tags: [Programming Contest]

---

---
tags: Programming Contest
---

# AtCoder Beginner Contest 170 題解

[題目連結](https://atcoder.jp/contests/abc170/tasks)

## 心得

賽後寫的。

## A, B, C

水題。

## D. Not Divisible

看了題解才想到，可以用 Sieve of Eratosthenes，因為 $A_i$ 最大才 $10^6$。

以下是一些要注意的或特判的 edge case：

```
1
1
5
2 2 2 3 3
5
2 2 2 4 4
5
1 1 1 1 2
```

使用 numpy 的 AC Code:

```python
import numpy as np
 
N = int(input())
A = list(map(int, input().split()))
A = np.int32(A)
 
vals, cnts = np.unique(A, return_counts=True)
 
if len(vals) == 1:
    print(0 if cnts[0] > 1 else 1)
elif vals[0] == 1:
    print(0 if cnts[0] > 1 else 1)
else:
    sat = np.ones(vals.max() + 1, dtype=bool)
    for x in vals:
        sat[2 * x::x] = False
    ans = (sat[vals] & (cnts == 1)).sum().item()
    print(ans)
```

## E. Smart Infants

照著題目模擬就是，問題主要是要用對資料結構。對於每個 kindergarden 我們需要一個可以高速插入、高速刪除、高速查詢最大值的容器。

C++ 的 multiset 符合所需。使用 c++ 的 multiset 要注意的一點是 **`set.erase(val)` 會刪除容器中所有值為 `val` 的 entries，若只想刪除一個值為 `val` 的 entry，要用 `set.erase(set.find(val))`**。

以下為 c++ 的 AC Code:

```cpp
#include <iostream>
#include <iterator>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;
    
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int N, Q;
    int M = 2 * 100000;
    cin >> N >> Q;
    
    auto belong = vector<int>(N, -1);
    auto rating = vector<int>(N, -1);
    auto evennesses = multiset<int>();
    auto kdrgrtns = vector<multiset<int>>(M);
    
    for (int i = 0; i < N; i++) {
        int A, B; 
        cin >> A >> B;
        B--;
        rating[i] = A;
        belong[i] = B;
        kdrgrtns[B].insert(A);
    }
    for (auto kdrgrtn: kdrgrtns) {
        if (!kdrgrtn.empty()) {
            evennesses.insert(*kdrgrtn.rbegin());
        }
    }
    
    while (Q--) {
        int C, D;
        cin >> C >> D;
        C--; D--;
    
        auto &kdrgrtn_src = kdrgrtns[belong[C]];
        auto &kdrgrtn_dst = kdrgrtns[D];
    
        evennesses.erase(evennesses.find(*kdrgrtn_src.rbegin()));
        kdrgrtn_src.erase(kdrgrtn_src.find(rating[C]));
        if (kdrgrtn_src.size() > 0) {
            evennesses.insert(*kdrgrtn_src.rbegin());
        }

        if (kdrgrtn_dst.size() > 0) {
            evennesses.erase(evennesses.find(*kdrgrtn_dst.rbegin()));
        }
        kdrgrtn_dst.insert(rating[C]);
        evennesses.insert(*kdrgrtn_dst.rbegin());
    
        belong[C] = D;
    
        cout << *evennesses.begin() << endl;
    }
    
    return 0;
}
```

## F. Pond Skater

這題馬上讓人想到是最短路徑，於是就刻了一個 Dijkstra，然後就會 TLE。
對於每個 vertex 我們都展開 4 * K 條邊實在太多了，所幸我們可以剪枝。

當我們在 `(r, c)`，嘗試用 `dis[r][c] + 1` 鬆弛 `dis[r + k * dr][c + k * dc]` 不成功時，我們不需要展開更大的 `k`，因為那些更遠的點（即 `k` 更大）不可能透過目前的路徑得到更佳的解。

寫成程式碼就是在展開邊的地方加入剪枝：

```cpp
// omit
for (int k = 1; k <= K; k++) {
    // omit
    if (dis[r + k * dr][c + k * dc] < dis[r][c] + 1) break;
    // omit
}
```

你就能 AC 了。官方題解我是沒有看懂，這題網路上的題解都是 BFS + 剪枝，我是覺得挺神奇的啦，最短路徑不是應該想到 Dijkstra 嗎？還有人說此剪枝方法不適用 Dijkstra，只能用在 BFS 上 (┛ಠ_ಠ)┛彡┻━┻

```cpp
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <functional>
#include <algorithm>
using namespace std;

typedef tuple<int, int, int> t3i;
const int INF = 0x3f3f3f3f;

int solve() {
    int H, W, K;
    cin >> H >> W >> K;

    int sr, sc, tr, tc;
    cin >> sr >> sc >> tr >> tc;
    sr--; sc--; tr--; tc--;

    vector<string> A(H);
    for (int r = 0; r < H; r++) {
        cin >> A[r];
    }

    auto deltas = vector<t3i>();
    deltas.push_back({+1, 0, 1});
    deltas.push_back({-1, 0, 1});
    deltas.push_back({0, +1, 1});
    deltas.push_back({0, -1, 1});

    auto dis = vector<vector<int>>(H, vector<int>(W, INF));
    auto que = priority_queue<t3i, vector<t3i>, greater<t3i>>();

    dis[sr][sc] = 0;
    que.push({0, sr, sc});

    while (!que.empty()) {
        auto [d, r, c] = que.top(); que.pop();
        if (d > dis[r][c]) continue;
        for (auto&& [dr, dc, w]: deltas) {
            for (int k = 1; k <= K; k++) {
                int nr = r + k * dr;
                int nc = c + k * dc;
                if (nr < 0 || nr >= H) break;
                if (nc < 0 || nc >= W) break;
                if (A[nr][nc] == '@') break;
                if (dis[nr][nc] < d + w) break;
                if (dis[nr][nc] > d + w) {
                    dis[nr][nc] = d + w;
                    que.push({dis[nr][nc], nr, nc});
                }
            }
        }
    }

    return ((dis[tr][tc] == INF) ? -1: dis[tr][tc]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cout << solve() << endl;

    return 0;
}
```

:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::