---
tags: Programming Contest
---
# Codeforces #662 (Div. 2) 題解
[題目連結](https://codeforces.com/contest/1393)
## 心得
久違地打 codeforces,然後狂掉 64 分,第三題解出來了,最後卡在沒開 long long,在 main test 倒數第二個測資被 hacked QQ。第一次看到在比賽開始後半小時內發了 3 個 announcements 來說明題意,由此可知敘述有夠難懂。我是不是被 AtCoder 寵壞了,受不了敘述這麼長的題目。在討論區[有人放了這圖](https://codeforces.com/blog/entry/81037?#comment-675778),有笑到 XD

## A. Rainbow Dash, Fluttershy and Chess Coloring
我觀察到只看矩陣的左上部份時,主對角線一次只會有一格被填,這是 critical path。詳細來說,當 n 是奇數時,第一次填與最後一次填是同樣顏色,所以 critical path 是從左上角到中心點的長度 (n + 1) / 2;當 n 是偶數時,第一次填與最後一次是不同顏色,所以 critical path 是對角線長度 n / 2 再 + 1。
## B. Applejack and Storages
維護目前容器中:
1. 次數 >= 8 的有幾種數字 `has_8`
2. 次數 6 or 7 的有幾種數字 `has_6`
3. 次數 4 or 5 的有幾種數字 `has_4`
4. 次數 2 or 3 的有幾種數字 `has_2`
注意一種數字最多只會出現在其中一項。可行的情況有以下 5 種:
```cpp
bool cond0 = has_8 >= 1;
bool cond1 = has_6 >= 2;
bool cond2 = has_6 == 1 and (has_4 >= 1 or has_2 >= 1);
bool cond3 = has_4 >= 2;
bool cond4 = has_4 == 1 and has_2 >= 2;
```
## C. Pinkie Pie Eats Patty-cakes
最小值最大化,標準二分搜的題目,問題是 check 怎麼寫。給定 shortest distance m,我們要判定 m 可不可行。我的方法是最優地去放數字,看能不能放成功。
所謂最優就是數字是 **週期性出現**(週期是 m + 1),且我優先放那些出現次數高的數字。假設某數字的出現次數是 `c`,且是第 `i` 高(最高=第 0 高,次高=第 1 高),那該數字要放的位置是等差數列 `(k - 1) * (m + 1) + i(1 <= k <= c)`
所謂能不能放成功,是指要放的那些位置必須 `< N`。實作上,針對每種數字,我只檢查他等差數列的最後一項,即 `(c - 1) * (m + 1) + i`。
好像這樣思考的人不多,看到不少人是用 priority queue 來實作 check,不明覺厲。
```python
from collections import Counter
def solve():
N = int(input())
A = list(map(int, input().split()))
C = sorted(Counter(A).values(), reverse=True)
def check(m):
for i, c in enumerate(C):
if (c - 1) * (m + 1) + i >= N:
return False
return True
lb, ub = -1, N
while ub - lb > 1:
m = (lb + ub) // 2
if check(m):
lb = m
else:
ub = m
return lb
TC = int(input())
for _ in range(TC):
print(solve())
```
## D. Rarity and New Dress
[Maximum Size Square](https://leetcode.com/problems/maximal-square/) 的變形,請參我之前的[題解](https://hackmd.io/@amoshyc/HyMBdb3-v)。
這題就是稍為變化了一下。設 `dp[r, c]` 為「菱形的最下端點在 `(r, c)` 時,最大的菱形的邊長」。轉移來自 `(r - 1, c - 1)`, `(r - 1, c)`, `(r - 1, c + 1)`, `(r - 2, c)`,因為當 `dp[r, c] = L` 時,這四個位置的 `L - 1` 菱形都可以擴張變成位於 `(r, c)` 的 `L` 菱形。
最大正方形中的「都為 1」變成「跟 `A[r, c]` 是同樣數字」。dp 的初使值也不一樣,此題中,每個位置的 dp 都大於等於 1,所以初使化為 1。我們一樣透過「檢查 dp 值」來取代「檢查擴張的那些格子符不符合條件」。
將這個 dp 建好後,題目所求恰好是這個 dp 表格每項的和。以下給出使用 C++17 的 AC code,除了最簡化的轉移方程,另一種轉移方程在註解中。
```cpp
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll solve() {
int N, M;
cin >> N >> M;
vector<string> A(N);
for (int i =0; i < N; i++) {
cin >> A[i];
}
auto dp = vector<vector<ll>>(N, vector<ll>(M, 1ll));
for (int r = 2; r < N; r++) {
for (int c = 1; c < M; c++) {
if (A[r][c] != A[r - 1][c - 1] or A[r][c] != A[r - 1][c] or
A[r][c] != A[r - 1][c + 1] or A[r][c] != A[r - 2][c]) {
continue;
}
dp[r][c] = max(dp[r][c],
min({
dp[r - 1][c - 1], dp[r - 1][c],
dp[r - 1][c + 1], dp[r - 2][c]
}) + 1
);
// ll L1 = dp[r - 1][c - 1];
// ll L2 = dp[r - 1][c];
// ll L3 = dp[r - 1][c + 1];
// ll L4 = dp[r - 2][c];
// if (L1 <= min({L2, L3, L4})) {
// dp[r][c] = max(dp[r][c], L1 + 1);
// }
// if (L2 <= min({L1, L3, L4})) {
// dp[r][c] = max(dp[r][c], L2 + 1);
// }
// if (L3 <= min({L1, L2, L4})) {
// dp[r][c] = max(dp[r][c], L3 + 1);
// }
// if (L4 <= min({L1, L2, L3})) {
// dp[r][c] = max(dp[r][c], L4 + 1);
// }
}
}
ll ans = 0;
for (int r = 0; r < N; r++) {
for (int c = 0; c < M; c++) {
ans += dp[r][c];
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << solve() << endl;
return 0;
}
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::