--- tags: Programming Contest --- # AtCoder Beginner Contest 129 題解 ## 心得 無事就來寫 atcoder。 ## A, B, C, D (⁎˃ᆺ˂) ## E. [Sum Equals Xor](https://atcoder.jp/contests/abc129/tasks/abc129_e) $a + b = a \text{ XOR } b$ 意謂著 $a$ 與 $b$ 不能有共同的都是 $1$ 的 digit。這常常出現,要背起來!不能都是 1,也就是不會有進位。然後這題就可以用 [Digit Dp](https://hackmd.io/@amoshyc/rJtXgC_hw) 解了。 $$ \begin{array}{r} &l_3 &l_2 &l_1 &l_0 \\ & a_3 &a_2 &a_1 &a_0 \\ & b_3 &b_2 &b_1 &b_0 \\ \end{array} $$ 在二進制中,設 $dp[i, f]$ = number of ($a_{i - 1} a_{i - 2}\cdots a_{0}$, $b_{i - 1} b_{i - 2} \cdots b_0$) such that 1. (f = 0) $a_{i - 1} a_{i - 2}\cdots a_{0} + b_{i - 1} b_{i - 2} \cdots b_0 \le l_{i - 1} l _{i - 2} \cdots l_{0}$ 2. (f = 1) $a_{i - 1} a_{i - 2}\cdots a_{0} + b_{i - 1} b_{i - 2} \cdots b_0 \gt l_{i - 1} l _{i - 2} \cdots l_{0}$ 枚舉第 $i$ 位 $a_i, b_i$ 所有可能情況,我們就可以遞推: $$ dp[i, f] \to dp[i + 1, f'] $$ 其中 $f'$ 代表 $a_i a_{i - 1} \cdots a_{0}+ b_i b_{i - 1} \cdots b_0$ 是不是大於 $l_i l_{i - 1} \cdots l_{0}$,而這只有有限幾種狀況而已。 ```python mod = 10 ** 9 + 7 L = [int(c) for c in input()[::-1]] N = len(L) dp = [[0, 0] for _ in range(N + 1)] # shaped [N + 1, 2] dp[0][0] = 1 for i in range(N): for f in range(2): # a[i] = 0, b[i] = 0 new_f = (L[i] == 0 and f == 1) dp[i + 1][new_f] += dp[i][f] dp[i + 1][new_f] %= mod # a[i] = 1, b[i] = 0 new_f = (L[i] == 0) or (L[i] == 1 and f == 1) dp[i + 1][new_f] += dp[i][f] dp[i + 1][new_f] %= mod # a[i] = 0, b[i] = 1 new_f = (L[i] == 0) or (L[i] == 1 and f == 1) dp[i + 1][new_f] += dp[i][f] dp[i + 1][new_f] %= mod print(dp[N][0]) ``` ## F. [Takahashi's Basics in Education and Learning](https://atcoder.jp/contests/abc129/tasks/abc129_f) 首先我們有等差數列的遞迴型式: $$ s_i = s_{i - 1} + B $$ 設 $t_i = s_0 s_1 \cdots s_i$ 的答案,彷造 [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method),我們得到以下轉移: $$ \begin{align} t_i &= 10^{digit(s_i)} t_{i - 1} + s_i \\ &= 10^{digit(s_i)} t_{i - 1} + s_{i - 1} + B \end{align} $$ $digit(s_i)$ 描述了 $s_i$ 是幾位數。我們可以將式子整理成矩陣型式: $$ \begin{pmatrix} t_i \\ s_i \\ 1 \end{pmatrix} = \begin{pmatrix} 10^{digit(s_i)} & 1 & B \\ 0 & 1 & B \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} t_{i - 1} \\ s_{i - 1} \\ 1 \end{pmatrix} $$ 如果式子中 3x3 矩陣的每一項都是定值,那我們可以透過矩陣快速幕求出答案 $t_{L - 1}$,但很可惜 $10^{digit(s_i)}$ 不滿足。 根據題意,因為 $A, B$ 都是正的,所以隨著 $i$ 增大, $digit(s_i)$ 只會單調遞增,而且題目又保證了 $s_i$ 最大只會是 10^18,所以有許多的相鄰的 $s_i$ 是相同位數!有相同位數的那些 $s_i$ 代表他們的 3x3 矩陣是相同的。以範測 [3, 7, 11, 15] 為例,我們可以寫出: $$ \begin{pmatrix} t_3 \\ s_3 \\ 1 \end{pmatrix} = \begin{pmatrix} 10^2 & 1 & B \\ 0 & 1 & B \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 10^2 & 1 & B \\ 0 & 1 & B \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 10^1 & 1 & B \\ 0 & 1 & B \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} t_0 \\ s_0 \\ 1 \end{pmatrix} $$ 先不考慮 mod,把數字填進去就會是: $$ \begin{pmatrix} 371115 \\ 15 \\ 1 \end{pmatrix} = \begin{pmatrix} 10^2 & 1 & 4 \\ 0 & 1 & 4 \\ 0 & 0 & 1 \end{pmatrix}^2 \begin{pmatrix} 10^1 & 1 & 4 \\ 0 & 1 & 4 \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} 3 \\ 3 \\ 1 \end{pmatrix} $$ 也就是說,從 $t_0=3, s_0 = 3$ 開始,我們經過一次一位數的變換(得到 $t_1 = 37, s_1= 7$),然後有二次二位數的變換,來到 $t_3=371115, s_3 = 15$。我們把相同位數的那些變換都合併在一樣(如同上述公式中,二位數矩陣應有兩個,但被合併成平方),然後用快速幕求出。 接下來問題就是在 $s_i$ 序列中,有幾個一位數?幾個二位數?幾個 $d$ 位數呢?而這可以透過簡單的不等式求出: $$ 10^{d-1}\le s_i \lt 10^d $$ 最終,等式右邊最多只會有 18 個基底矩陣,因此時間完全來得及。實作上,就是建出這 18 個基底矩陣,並算出各要乘幾次,然後用矩陣快速幕將之乘出來。 ```python def matmul(A, B, mod): N, D, M = len(A), len(A[0]), len(B[0]) C = [[0 for _ in range(M)] for _ in range(N)] for r in range(N): for c in range(M): for i in range(D): C[r][c] += A[r][i] * B[i][c] % mod C[r][c] %= mod return C def powmat(A, p, mod): N = len(A) ans = [[0 for _ in range(N)] for _ in range(N)] for i in range(N): ans[i][i] = 1 base = A while p: if p & 1: ans = matmul(base, ans, mod) base = matmul(base, base, mod) p >>= 1 return ans L, A, B, M = map(int, input().split()) pivots = [(10 ** d - A - 1) // B for d in range(1, 20)] ans = [[A], [A], [1]] # t0, s0, 1 prev_pos = 0 for i in range(len(pivots)): if pivots[i] < 0: continue curr_pos = min(pivots[i], L - 1) cnt = curr_pos - prev_pos # number of transformation mat = powmat([[pow(10, i + 1, M), 1, B], [0, 1, B], [0, 0, 1]], cnt, M) ans = matmul(mat, ans, M) prev_pos = curr_pos if curr_pos >= L - 1: break print(ans[0][0]) ``` :::success [< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/) :::