---
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/)
:::