---
tags: Programming Contest
---
# AtCoder Beginner Contest 178 題解
[題目連結](https://atcoder.jp/contests/abc178/tasks)
## 心得
第三題卡了我好久好久,最後跑去先解第四題,10 分鐘 AC,才又回來想第三題。高中就知道我排列組合不好,現在還是不會,我真是太失敗了 ੨( ・᷄ ︵・᷅ )シ,我到底該正著算呢?還是反著算呢?這樣算到底會不會重覆算呢?是不是要枚舉 0 的數量,用重複組合?第五題沒解出來,聽說是經典題,讓我好好研究研究。
## A, B
ʘ‿ʘ
## C. Ubiquity
一開始的想法是正著算:在 N 個位置中任取兩個位置,放 0 與 9,其他位置數字任填,所以方法數是 ${N \choose 2} \cdot 2! \cdot 10^{N-2}$,然後範測一直過不了,換了各種方式都還是不行。
等到第四題解完回來,決定看看 N=3, N=4, N=5 時的答案是多少,寫了個程式爆開,答案分別是 54, 974, 140670 就知道正著算會錯,上述式子在 N = 5 時會產出 3 個 0。也想到了在 N = 3 時,pattern [0, 9, x] 與 [0, x, 9] 會重覆算到相同序列。
反著解,即用扣的,再應用排容:
答案
= 有出現 0 **且** 有出現 9 的序列個數
= 全部序列個數 - 「沒出現 0 **或** 9 的序列個數」
= 全部序列個數 - 「沒出現 0 的序列個數 」- 「沒出現 9 的序列個數」+ 「沒出現 0 **且** 沒出現 9 的序列個數」
數學式即為:
$$
(10^N) - (9^N) - (9^N) + (8^N)
$$
```python
N = int(input())
M = 10**9 + 7
ans = pow(10, N, M)
ans = ans + M - pow(9, N, M)
ans = ans + M - pow(9, N, M)
ans = ans + pow(8, N, M)
ans = ans % M
print(ans)
```
## D. Redistribution
我的解法跟其他人似乎不太一樣。
把 S = 1~9 的所有序列畫出來,會發現數字 `i` 的那些序列,以序列首項來分類會有規律:
1. 首項為 `3`,剩餘序列是數字 `i - 3` 的那些序列
2. 首項為 `4`,剩餘序列是數字 `i - 4` 的那些序列
3. ...
4. 首項為 `i - 3`,剩餘序列是數字 `3` 的那些序列
5. 序列 `[i]`
於是,令數字 $i$ 的序列個數為 $f(i)$,那 $f(i) = f(i - 3) + f(i - 4) + ... + f(3) + 1$,其中 $f(3) = f(4) = f(5) = 1$。建個 dp 表格求出 $f(S)$ 即可,時間複雜度為 $O(S^2)$,雖然可以另外維護 dp 的前綴和來取代加總部份的計算,讓整體時間變成 $O(S)$,但這題 $S$ 很小直接寫下去即可。實作上,記得特判 $S < 6$ 的那些情況。
$O(S^2)$: 76ms, pypy
```python
N = int(input())
M = 10**9 + 7
if N < 3:
print(0)
elif N < 6:
print(1)
else:
dp = [0] * (N + 1)
dp[3] = 1
dp[4] = 1
dp[5] = 1
for i in range(6, N + 1):
dp[i] = sum(dp[3:i - 3 + 1]) + 1
dp[i] %= M
print(dp[-1] % M)
```
$O(S)$: 63 ms, pypy
```python
N = int(input())
M = 10**9 + 7
if N < 3:
print(0)
elif N < 6:
print(1)
else:
dp = [0] * (N + 1)
dp[3] = 1
dp[4] = 1
dp[5] = 1
P = dp[3]
for i in range(6, N + 1):
dp[i] = (P + 1) % M
P = (P + dp[i - 2]) % M
print(dp[-1] % M)
```
## E. Dist Max
網上各種題解都想不通,我理解點對 $(x_1, y_1), (x_2, y_2)$ 的曼哈頓距離**在 $x_1 \ge x_2, y_1 \ge y_2$ 的情況下**可以簡化成 $max(x_1 + y_1) - min(x_2 + y_2)$,但怎麼可以拿**所有點對**的 $x+y$ 的最大值減最小值(看到題解的程式都是這樣寫的),就說是一組可能的答案呢?該推論是有條件的,點對必須滿足 $x_1 \ge x_2, y_1 \ge y_2$!這中間一定少了什麼。
直到剛剛,我看到 [lucifer1004](https://codeforces.com/blog/entry/82566?#comment-695506) 的[題解](https://cp-wiki.vercel.app/tutorial/atcoder/ABC178/),才恍然大悟,是因為可以將絕對值函數轉成 max 函數。
知道這個事實上,透過一些代數運算,我得到我可以理解的題解:
### 引理 1
$|a| = max(-a, +a)$
### 引理 2
平面上任兩點 $(x_i, y_i), (x_j, y_j)$ 的曼哈頓距離 $|x_i - x_j| + |y_i - y_j|$ 是 $max(u_i - u_j, v_i - v_j, v_j - v_i, u_j - u_i)$,其中 $u_i = x_i + y_i, v_i = x_i - y_i$。
$$
\begin{align}
&|x_i - x_j| + |y_i - y_j| \\
&= max(x_i - x_j, x_j - x_i) + max(y_i - y_j, y_j - y_i) \\
&= max(
(x_i - x_j + y_i - y_j),
(x_i - x_j + y_j - y_i),
(x_j - x_i + y_i - y_j),
(x_j - x_i + y_j - y_i)
) \\
&= max(
((x_i + y_i) - (x_j + y_j)),
((x_i - y_i) - (x_j - y_j)),
((x_j - y_j) - (x_i - y_i)),
((x_j + y_j) - (x_i + y_j))
) \\
&= max(u_i - u_j, v_i - v_j, v_j - v_i, u_j - u_i)
\end{align}
$$
### 推導
題目所求是要找出點對的最大距離,寫成最佳化的型式:
$$
\begin{align}
&\max_{0 \le i \lt N, 0 \le j \lt N} |x_i - x_j| + | y_i - y_j| \\
&= \max_{0 \le i \lt N, 0 \le j \lt N} max(u_i - u_j, v_i - v_j, v_j - v_i, u_j - u_i) \\
&= max(
\max_{0 \le i \lt N, 0 \le j \lt N} (u_i - u_j),
\max_{0 \le i \lt N, 0 \le j \lt N} (v_i - v_j),
\max_{0 \le i \lt N, 0 \le j \lt N} (v_j - v_i),
\max_{0 \le i \lt N, 0 \le j \lt N} (u_j - u_i)) \\
&= max(
(\max_{0 \le i \lt N} u_i - \min_{0 \le j \lt N} u_j),
(\max_{0 \le i \lt N} v_i - \min_{0 \le j \lt N} v_j),
(\max_{0 \le j \lt N} v_j - \min_{0 \le i \lt N} v_i),
(\max_{0 \le j \lt N} u_j - \min_{0 \le i \lt N} u_i))
\end{align}
$$
其中 $\max_{0 \le i \lt N} u_i$ 與 $\max_{0 \le j \lt N} u_j$ 是同一個東西,$\max_{0 \le i \lt N} v_i$ 與 $\max_{0 \le j \lt N} v_j$ 是同一個東西,以此類推。我們可以把 $j$ 完全換成 $i$,最後得到:
$$
max(
(\max_{0 \le i \lt N} u_i - \min_{0 \le i \lt N} u_i),
(\max_{0 \le i \lt N} v_i - \min_{0 \le i \lt N} v_i)
)
$$
這個式子可以在 $O(N)$ 時間求出。佩服自己想出把最佳化的 $\max$ 往內移、把斜體的 $max$ 往外提!哇哈哈哈 ─=≡Σ((( つ•̀ω•́)つ 網上都沒人說到這個部份。但我認為有了這個,整個推導才是合理的,才會變成求 $u, v$ 的最大值與最小值。
```python
N = int(input())
xs, ys = [], []
for _ in range(N):
x, y = map(int, input().split())
xs.append(x)
ys.append(y)
U = [x + y for x, y in zip(xs, ys)]
V = [x - y for x, y in zip(xs, ys)]
ans = max([max(U) - min(U), max(V) - min(V)])
print(ans)
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::