---
tags: Programming Contest
---
# AtCoder Beginner Contest 222 題解
## 心得
久違地打了一場…只解出四題,慘。
## A, B, C
(⋋▂⋌)
## D. [Between Two Arrays](https://atcoder.jp/contests/abc222/tasks/abc222_d)
設 $dp[i, j]$ = 合法的 $C_0, C_1, \cdots, C_i$ 數量,且 $C_i = j$。因為 $C_{i - 1}$ 需要少於 $j$ 就可以,可以寫出轉移:
$$
dp[i, j] = dp[i - 1][0] + dp[i - 1][1] + \cdots + dp[i - 1][j]
$$
等式右邊可以隨著 $j$ 增加,用前綴和來記錄。實作上 dp 可以使用二個一維陣列來壓縮空間。這題如果估時間複雜度,是 $3000 \times 3000$,害我在比賽時一直覺得這樣解不對,最後想不到其他硬寫下去就 AC 了……。
```python
mod = 998244353
N = int(input())
A = [int(x) for x in input().split()]
B = [int(x) for x in input().split()]
prev = [0 for _ in range(3000 + 1)]
for c in range(A[0], B[0] + 1):
prev[c] = 1
for i in range(1, N):
curr = [0 for _ in range(3000 + 1)]
S = sum(prev[: A[i]]) % mod
for c in range(A[i], B[i] + 1):
S += prev[c]
S %= mod
curr[c] = S
prev = curr
print(sum(curr[A[i] : B[i] + 1]) % mod)
```
## E. [Red and Blue Tree](https://atcoder.jp/contests/abc222/tasks/abc222_e)
比賽中只想到去統計路徑中,各個邊出現的次數,然後看看這些次數 $c_0, c_1, \cdots, c_{N-1}$ 不能不能組出合理的 R 與 B,也想到了可以用 dp 去解這個組合問題。但就是漏了一個條件,害我一直以為要枚舉 R 或枚舉 B……可惜。
漏的條件是 $R + B = c_0 + c_1 + \cdots + c_{N-1}$。這個式子與題目所給的 $R - B = K$ 可以聯立方程,解出 $R = \frac{K + S}{2}$,其中 $S = c_0 + c_1 + \cdots + c_{N-1}$。也就是說給定路徑與 $K$ 後,$R, B$ 就被確定了!$R, B$ 都是定值!
而 dp 的部份也是簡單的,設 $dp[i, j]$ 為只考慮 $c_0 + c_1 + \cdots + c_i$ 組出 $j$ 的方法數。考慮用或不用 $c_i$ 可以寫出轉移方程:
$$
dp[i, j] = dp[i - 1, j] + dp[i - 1, j - c_i]
$$
如同無界背包,實作上我將這個 dp 的空間壓到一維,不壓好像也可以就是了。這題時間複雜度也是很極限,dp 的部份就是 $10^8$,一般 atcoder 的題目不會卡這麼極限的說。另外小心無解的情況,與路徑中可能會有自環!尤其是後者,我被這個卡了許久,例如測資
```
5 5 0
1 1 1 1 1
1 2
2 3
3 4
4 5
```
的答案應為 16。
```python
N, M, K = map(int, input().split())
A = [int(x) - 1 for x in input().split()]
G = [[] for _ in range(N)]
C = dict()
for _ in range(N - 1):
u, v = map(int, input().split())
u, v = u - 1, v - 1
G[u].append(v)
G[v].append(u)
C[(min(u, v), max(u, v))] = 0
for i in range(1, M):
s, t = A[i - 1], A[i]
stack = [s]
visit = [False for _ in range(N)]
parent = [-1 for _ in range(N)]
visit[s] = True
parent[s] = -1
while len(stack) > 0:
u = stack.pop()
if u == t:
break
for v in G[u]:
if not visit[v]:
stack.append(v)
parent[v] = u
visit[v] = True
path = [t]
while parent[path[-1]] != -1:
path.append(parent[path[-1]])
path.reverse()
for u, v in zip(path[:-1], path[1:]):
C[(min(u, v), max(u, v))] += 1
C = list(C.values())
S = sum(C)
# dp[i][j] = number of ways to form j from C[0], C[1], ..., C[i]
# dp[i][j] = dp[i - 1][j - C[i]]
V = 10 ** 5
dp = [0 for _ in range(V + 1)]
dp[0] = 1
mod = 998244353
for i in range(len(C)):
for j in range(V, C[i] - 1, -1):
dp[j] += dp[j - C[i]]
dp[j] %= mod
if (K + S) % 2 == 1 or (K + S) < 0:
print(0)
else:
print(dp[(K + S) // 2])
```
## F. [Expensive Expense](https://atcoder.jp/contests/abc222/tasks/abc222_f)
比賽中就有想到這題跟 diameter/eccentricity 很有關係,但一直想不到怎麼解決節點上的通行費。
官方題解首先利用了 diameter 的性質,在一個無向圖中,假設 $(s, t)$ 是一條 diameter,則對任一個點 $u$,恆有:
$$
ecc(u) = max(d(u, s), d(u, t))
$$
其中 $d()$ 是距離函式,此題包含著終點節點的通行費。
有了這個式子,我們的目標是找出一個 diameter $(s, t)$,然後使用 Dijkstra 找出以 $s$ 為出發點,所有點的最短距離 $dist_s$ ,與從 $t$ 出發所有點的最短距離 $dist_t$,然後任何一點 $u$ 的所求即是 $dist_s(u)$ 與 $dist_t(u)$ 中較大者。$(s, t)$ 可以透過二次 dijkstra 得到,如同在 Tree 中一樣。
```python
N = int(input())
G = [[] for _ in range(N)]
for _ in range(N - 1):
a, b, c = map(int, input().split())
a, b = a - 1, b - 1
G[a].append((b, c))
G[b].append((a, c))
D = [int(x) for x in input().split()]
dist = dijkstra(G, 0)
dist = [d1 + d2 for d1, d2 in zip(dist, D)]
max_dist = max(dist[u] for u in range(N) if u != 0)
s = next(u for u in range(N) if dist[u] == max_dist and u != 0)
dist = dijkstra(G, s)
dist = [d1 + d2 for d1, d2 in zip(dist, D)]
max_dist = max(dist[u] for u in range(N) if u != s)
t = next(u for u in range(N) if dist[u] == max_dist and u != s)
dist_s = dijkstra(G, s)
dist_t = dijkstra(G, t)
diameter = dist_s[t]
for u in range(N):
if u == s:
print(diameter + D[t])
elif u == t:
print(diameter + D[s])
else:
print(max(dist_s[u] + D[s], dist_t[u] + D[t]))
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::