---
tags: Programming Contest
---
# AtCoder Beginner Contest 181 題解
[題目連結](https://atcoder.jp/contests/abc181)
## 心得
因為沒同學/朋友要一起參戰,比賽時只來看了一圈題目沒 submit。
## A, B, C
水題。
## D. Hachi
看完題目想說這麼簡單,怎麼 Standings 上一堆人都先 WA 才 AC,看我一次 AC,然後我就 WA 了三次 (┛ಸ_ಸ)┛彡┻━┻
基本想法就是枚舉 8 在 1000 以內的所有倍數,看有沒有一個倍數 x 所需要的 digits 能被 S 滿足。
這個解法的問題在於 S < 1000 時會有問題,例如 S = '516',答案應該要是 No,但按上述方法會在 x = 16 時發現可以滿足。
一個解決的方法就是分 len(S) = 1, len(S) = 2, len(S) = 3, len(S) > 3 的情況分別討論。我這邊的解法是利用 S 中不可能有 0 的特性,將 x 補前綴 0,補到 min(len(S), 3),再去看 S 能不能滿足 x 的 digits。
```python
from collections import defaultdict
S = input()
cnt = defaultdict(int)
for c in S:
cnt[c] += 1
for x in range(8, 1000, 8):
strx = str(x).ljust(min(len(S), 3), '0')
need = defaultdict(int)
for c in strx:
need[c] += 1
if all(cnt[c] >= need[c] for c in need.keys()):
print('Yes')
break
else:
print('No')
```
## E. Transformable Teacher
若給定偶數個數字 A,兩兩配對,使差的總和最小化的方法為:
設 S = sorted(A),則 S[i] 與 S[i ^ 1] 配對。以下給出證明。
設有四個數字 `x < y < z < w`,那我們配對的方式有 3 種:
1. `[x, y], [z, w]: (w - z) + (y - x)`
2. `[x, z], [y, w]: (w - y) + (z - x)`
3. `[x, w], [y, z]: (z - y) + (w - x)`
每一種配對所得到的差的總和都有 `+ w - x`,讓我們將之消掉:
1. `[x, y], [z, w]: (y - z)`
2. `[x, z], [y, w]: (z - y)`
3. `[x, w], [y, z]: (z - y)`
明顯第一種配對方式是最小。
這個結論引導我們設計出這樣的方法:
1. 將 H 由小排到大
2. 預計算 `pref[i]` = `H[:i]` 從左至右兩兩配對的結果,`pref[i]` 只在 `i` 是奇數時有效
3. 預計算 `suff[i]` = `H[-i:]` 從右至左兩兩配對的結果,`suff[i]` 只在 `i` 是奇數時有效
4. 枚舉 w,二分搜 w 在 H 中的位置為 `idx`。
5. 若 `idx` 是偶數,則 w $\cup$ H 的結果為 `pref[idx - 1] + suff[idx + 1] + (H[idx] - w)`
6. 若 `idx` 是奇數,則 w $\cup$ H 的結果為 `pref[idx - 2] + suff[idx] + (w - H[idx - 1])`
後面這些數字的部份,拿範測推一下即可。
小心 `idx` 是 0 或 N 的情況,我這邊將 `pref`, `suff` 超出界限的部份設成 0。
```python
from bisect import bisect_left
from collections import defaultdict
N, M = map(int, input().split())
H = [int(x) for x in input().split()]
W = [int(x) for x in input().split()]
H.sort()
pref = defaultdict(int)
pref[0] = -H[0]
for i in range(1, N):
if i % 2 == 1:
pref[i] = pref[i - 1] + H[i]
else:
pref[i] = pref[i - 1] - H[i]
suff = defaultdict(int)
suff[N - 1] = H[N - 1]
for i in range(N - 2, -1, -1):
if i % 2 == 1:
suff[i] = suff[i + 1] - H[i]
else:
suff[i] = suff[i + 1] + H[i]
ans = 10 ** 9
for w in W:
idx = bisect_left(H, w)
if idx % 2 == 1:
val = pref[idx - 2] + suff[idx] + w - H[idx - 1]
else:
val = pref[idx - 1] + suff[idx + 1] + H[idx] - w
ans = min(ans, val)
print(ans)
```
## F. Silver Woods
Credit: 討論區 unstoppable_1000 給出的[題解](https://codeforces.com/blog/entry/84246#comment-717623)。
二分搜圓的半徑 r,然後枚舉每一個 nail,然後看他跟其他 nail 或邊界是不是距離 2r 以內,若是的話,說明圓是通不過他們之間的 gap 的,將他們視為一群。最後看上方邊界與下方邊界是不是一群就知道這個 r 可不可行。
想不到 Disjoint set 可以用在這種地方,拿來判斷上方邊界與下方邊界有沒有連通,學到了。
```python
from math import sqrt
class DisjoinSet:
def __init__(self, N):
self.par = [-1] * N
self.sz = [1] * N
def root(self, x):
if self.par[x] < 0:
return x
else:
self.par[x] = self.root(self.par[x])
return self.par[x]
def unite(self, a, b):
a = self.root(a)
b = self.root(b)
if a == b:
return
if self.sz[a] > self.sz[b]:
a, b = b, a
self.par[a] = b
self.sz[b] += self.sz[a]
def same(self, a, b):
return self.root(a) == self.root(b)
def size(self, x):
return self.sz[self.root(x)]
def __str__(self):
from collections import defaultdict
clusters = defaultdict(list)
for x in range(N):
clusters[self.root(x)].append(x)
return str(list(clusters.values()))
N = int(input())
xs, ys = [], []
for _ in range(N):
x, y = map(float, input().split())
xs.append(x)
ys.append(y)
def check(r):
dsu = DisjoinSet(N + 2)
for i in range(N):
for j in range(i + 1, N):
if sqrt((xs[i] - xs[j]) ** 2 + (ys[i] - ys[j]) ** 2) < 2 * r:
dsu.unite(i, j)
for i in range(N):
if abs(ys[i] - 100) < 2 * r:
dsu.unite(i, N + 0) # upper boundary
if abs(ys[i] - (-100)) < 2 * r:
dsu.unite(i, N + 1) # lower boundary
return dsu.root(N + 0) != dsu.root(N + 1)
lb, ub = 0, 200
for _ in range(50):
m = (lb + ub) / 2
if check(m):
lb = m
else:
ub = m
print('{:.9f}'.format(lb))
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::