---
# System prepended metadata

title: AtCoder Regular Contest 106 題解
tags: [Programming Contest]

---

---
tags: Programming Contest
---

# AtCoder Regular Contest 106 題解

[題目連結](https://atcoder.jp/contests/arc106)

## 心得

第一題竟然花了我 20 分鐘，還好最終有解出三題，排 872，積分 +87。

## A. 106

把所有可能爆開即可。一開始的想法是枚舉 b，然後用 `log`, `floor` 之類的函式的把對應的 a 找出來，但這個寫法會在 6 筆 test cases WA，debug 了好久找不出原因，八成是被 double 卡掉的關係。

```python
ans = dict()
for a in range(1, 38):
    for b in range(1, 26):
        ans[pow(3, a) + pow(5, b)] = f'{a} {b}'

N = int(input())
print(ans.get(N, -1))
```

## B. Values

就看圖的各個連通區塊內的 A 值總和是不是 = B 值總和，因為連通區塊內的每個頂點可以互相交換值。

```python
N, M = map(int, input().split())
A = [int(x) for x in input().split()]
B = [int(x) for x in input().split()]

adj = [[] for _ in range(N)]
for _ in range(M):
    u, v = map(int, input().split())
    u, v = u - 1, v - 1
    adj[u].append(v)
    adj[v].append(u)


def solve():
    vis = [False] * N
    for root in range(N):
        if vis[root]:
            continue

        vis[root] = True
        src, tgt = 0, 0
        stack = [root]
        while len(stack) > 0:
            u = stack.pop()
            src += A[u]
            tgt += B[u]
            for v in adj[u]:
                if not vis[v]:
                    vis[v] = True
                    stack.append(v)

        if src != tgt:
            return 'No'

    return 'Yes'


print(solve())
```

## C. Solutions


我嘗試造出二者不一致的情況，發現以下模式：

![](https://i.imgur.com/9Js8jf2.png)

而這樣的模式使用 X 個線段，得到 M = 上排 - 下排 = (X - 1) - 1 = X - 2。所以限定 N 個線段時，能解出的最大 M 是 N - 2。當 M 小於 N - 2 時，就放 M + 1 個線段在上排，一個線段在下排（藍線），剩下的線段就讓他們不相交的放於下排（藍線）右邊。而 M < 0 的情況我實在找不出這樣的可能，所以就猜無解了。

一次過 `(~‾▿‾)~`，submit 時還以為會 WA。


```python
N, M = map(int, input().split())

if M == 0:
    segs = [(2 * i + 10, 2 * i + 11) for i in range(N)]
    print('\n'.join([f'{l} {r}' for l, r in segs]))
elif 1 <= M <= N - 2:
    N_top = M + 1
    segs = [(2 * i + 10, 2 * i + 11) for i in range(N_top)]
    
    L, R = segs[0][0] - 1, segs[-1][1] + 1
    segs.append((L, R))

    N_rest = N - M - 2
    segs.extend((2 * i + R + 10, 2 * i + R + 11) for i in range(N_rest))

    print('\n'.join([f'{l} {r}' for l, r in segs]))
else:
    print(-1)
```

## D. Powers

那我們先解決一個問題：給定長度為 N 的陣列 A 與 B，求 $\sum_{i = 0}^{N-1} \sum_{j=0}^{N-1} A_i B_j$。我們可以利用分配律的特性，在 $O(N)$ 算出答案，因為所求等價於 $\left( \sum_{i=0}^{N-1} A_i \right) \left( \sum_{j=0}^{N-1} B_j \right)$。我們還可以推導出以下式子：

$$
\begin{align}
\sum_{0 \le i \lt N, 0 \le j \lt N} A_i B_j &= \left( \sum_{i=0}^{N-1} A_i \right) \left( \sum_{j=0}^{N-1} B_j \right)
\\
\sum_{0 \le i, j \lt N, i \ne j} A_i B_j &= \left( \sum_{i=0}^{N-1} A_i \right) \left( \sum_{j=0}^{N-1} B_j \right) - \left( \sum_{i=0}^{N - 1} A_i B_i \right)
\\
\sum_{0 \le i \lt j \lt N} A_i B_j &= \frac{\left( \sum_{i=0}^{N-1} A_i \right) \left( \sum_{j=0}^{N-1} B_j \right) - \left( \sum_{i=0}^{N - 1} A_i B_i \right)}{2}
\end{align}
$$

而這題就會用到上述的最後一個式子。讓我們將題目所求用二項式定理展開：

$$
\sum_{0 \le i \lt j \lt N} (A_i + A_j)^X =
\sum_{0 \le i \lt j \lt N} \sum_{k=0}^{X} {X \choose k} A_i^{X - k} A_j^k
$$

將二項式的部份往外提，再應用我們前面的式子。

$$
\begin{align}
&= \sum_{k=0}^{X} {X \choose k} \sum_{0 \le i \lt j \lt N}  A_i^{X - k} A_j^k 
\\
&= 
\sum_{k=0}^{X} {X \choose k}
\frac{\left( \sum_{i=0}^{N-1} A_i^{X-k} \right) \left( \sum_{j=0}^{N-1} A_j^k \right) - \left( \sum_{i=0}^{N - 1} A_i^{X-k} A_i^k \right)}{2}
\\
&=
\frac{1}{2}
\sum_{k=0}^{X} {X \choose k}
\left(
\left( \sum_{i=0}^{N-1} A_i^{X-k} \right) \left( \sum_{j=0}^{N-1} A_j^k \right) - \left( \sum_{i=0}^{N - 1} A_i^X \right)
\right)
\end{align}
$$

如果我們預計算 $S_c = \sum_{i=0}^{N - 1} A_i^c, c \in [0, K]$，那上述式子就可以改寫成：

$$
\frac{1}{2} \sum_{k=0}^{X} {X \choose k} S_{X - k} S_k S_X
$$

此式可以在 $O(K)$ 時間求出。因此這題的整體時間為 $O(NK + K^2)$ 。

比賽時，我只想出了往外提的部份，沒想出後面 QQ。


```cpp
#include <algorithm>
#include <atcoder/modint>
#include <iostream>
#include <tuple>
#include <vector>
using namespace std;

using mint = atcoder::modint998244353;

template <typename T> struct CombTool {
    vector<T> fact;
    vector<T> finv;

    CombTool(int V) {
        fact = vector<T>(V, 1);
        finv = vector<T>(V, 1);
        for (int i = 1; i < V; i++) {
            fact[i] = fact[i - 1] * i;
        }
        finv[V - 1] = fact.back().inv();
        for (int i = V - 2; i >= 0; i--) {
            finv[i] = finv[i + 1] * (i + 1);
        }
    }

    T comb(int a, int b) { return fact[a] * finv[b] * finv[a - b]; }
    T perm(int a, int b) { return fact[a] * finv[a - b]; }
    T hcomb(int a, int b) { return comb(a + b - 1, b); }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int N, K;
    cin >> N >> K;
    auto A = vector<int>(N);
    for (int i = 0; i < N; i++) {
        cin >> A[i];
    }

    auto tool = CombTool<mint>(300 + 10);
    auto sum_of_pow = vector<mint>(K + 1, 0);
    sum_of_pow[0] = N;
    for (int i = 0; i < N; i++) {
        mint a = A[i];
        for (int c = 1; c <= K; c++) {
            sum_of_pow[c] += a;
            a = a * A[i];
        }
    }

    for (int x = 1; x <= K; x++) {
        mint ans = 0;
        for (int k = 0; k <= x; k++) {
            mint val = 0;
            val = val + sum_of_pow[x - k] * sum_of_pow[k] - sum_of_pow[x];
            val = val * tool.comb(x, k);
            ans += val;
        }
        ans = ans / 2;
        cout << ans.val() << endl;
    }

    return 0;
}
```

:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::