---
tags: Programming Contest
---
# Codeforces #663 (Div. 2) 題解
[題目連結](https://codeforces.com/contest/1391)
## 心得
又繼續掉分了 (╯°□°)╯︵ ┻━┻
第二題卡牆,明明題目說只有 `R`, `D`,我思考時卻一直想成會有 `R`, `L`, `D`, `U`,然後我就去刻了個 BFS,卻發現會錯,還好最後有解出這題。第四題可惜,有想到 `N>=4 and M >= 4` 時無解,卻沒想出如何高速處理 `3xM` 或 `Nx3` 的情況,竟然得 dp…最後稱讚一下 editorial,比賽一結束就有了,而且有影片版本,雖然印度口音很重,但有字幕!
## A. Suborrays
看到官方題解嚇到了:Every permutation is good。比賽時我是將大的數字與小的數字交錯出現,沒仔細思考為什麼會對 XD,又是 bit 的,又是個數的,很難扯在一起啊。
## B. Fix You
因為只有 `R`, `D`,所以行李最後只有三種可能:
1. 到達右下角
2. 從矩陣右方離開
3. 從矩陣下方離開
所以答案就是將矩陣右方都改成向下、矩陣下方都改成向右。
## C. Cyclic Permutations
比賽時原本以為解不出來,後來竟然被我想出來了,值得慶祝,還好沒放棄 XD。
以下列出 **不是** cyclic permutation 的情況:
```
N = 3
[1 2 3] [3 2 1]
[1 3 2] [2 3 1]
```
```
N = 4
[1 2 3 4] [4 3 2 1]
[1 2 4 3] [3 4 2 1]
[1 3 4 2] [2 4 3 1]
[1 4 3 2] [2 3 4 1]
```
可以發現二點:
1. 左方的那些 permutations 與右方的那些 permutations 互為對稱。
2. 把 `N = 4` 的左方那些 permutations 的 1 拿掉,剩下數字減 1,會變成 `N = 3` 的所有 permutations。
可以推論 `N=k` 時 **不是** cyclic permutation 的數量是 `N = k - 1` 時的二倍。我們又知道 N = 3 時是 4,於是得到公式解 :`N = k` 時,**不是** cyclic permutation 的數量為 $4 \cdot 2^{k-3} = 2^{k - 1}$。
題目所求就是 $N! - 2^{N - 1}$。
```python
mod = 10**9 + 7
N = int(input())
fact = [1] * (N + 1)
for i in range(1, N + 1):
fact[i] = fact[i - 1] * i % mod
ans = fact[N]
ans = ans - pow(2, N - 1, mod) + mod
ans = ans % mod
print(ans)
```
## D. 505
官方題解與影片都寫得不錯。
首先 `N >= 4 and M >= 4` 無解,因為他可以分成偶數個不重疊的 2x2 矩陣,偶數乘上奇數是偶數。另外,`N == 1 or M == 1`,直接輸出 0,給定的矩陣必為 good。剩下的情況得用 bitmask dp 來處理。
設 `dp[c][mask]` 為使前 `c` 個 cols 變成 good,且第 `c` 個 col 變成 `mask`,所需的最小操作數。mask 是 bitmask,例如 `mask = 0b110`,那代表我們要將該 col 變成 `[1, 1, 0]`。題目所求為 `min(dp[M - 1][mask] for mask in range(1 << N))`。
針對`dp[c][curr_mask]` 時,其轉移為枚舉 col `c - 1` 所有**可行** mask,取最小值,再加上將 col `c` 變成 `curr_mask` 的操作數:
```python
dp[i][curr_mask] = min(
[
dp[i - 1][prev_mask]
for prev_mask in range(1 << N)
if is_valid(prev_mask, curr_mask, N)
]
) + make(state[i], curr_mask)
```
所謂 **可行** 是指 col `c` 的 `curr_mask` 與 col `c - 1` 的 `prev_mask` 不會構成總和為偶數的 2x2 子矩陣。實作上,可以透過一些位元操作在 O(1) 算出。將 col `c` 變成 `curr_mask` 的操作數也可以透過 `popcount` 與 `xor` 得到。
底下程式碼是 pypy。
```python
def is_valid(prev_mask, curr_mask, N):
bits = prev_mask ^ curr_mask
if N == 2:
return bits != 0b00 and bits != 0b11
else:
check1 = bits != 0b000 and bits != 0b001 and bits != 0b100
check2 = bits != 0b111 and bits != 0b110 and bits != 0b011
return check1 and check2
def solve():
N, M = map(int, input().split())
A = [list(map(int, input())) for _ in range(N)]
if N >= 4 and M >= 4:
return -1
if N == 1 or M == 1:
return 0
state = [0] * M
for c in range(M):
for r in range(N):
if A[r][c] == 1:
state[c] |= 1 << (N - 1 - r)
popcounts = [bin(x).count('1') for x in range(1 << N)]
dp = [[N * M] * (1 << N) for _ in range(M)]
for mask in range(1 << N):
dp[0][mask] = popcounts[state[0] ^ mask]
for i in range(1, M):
for curr_mask in range(1 << N):
dp[i][curr_mask] = min(
[
dp[i - 1][prev_mask]
for prev_mask in range(1 << N)
if is_valid(prev_mask, curr_mask, N)
]
) + popcounts[state[i] ^ curr_mask]
return min(dp[-1])
print(solve())
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::