---
tags: Programming Contest
---
# AtCoder Beginner Contest 177 題解
[題目連結](https://atcoder.jp/contests/abc177/tasks)
## 心得
賽後才寫的。跑去跟我弟打球,就沒打這個比賽了
## A, B
略
## C. Sum of product of pairs
常見的手法,改成這麼想:對於每個 A[j],會貢獻多少值到答案中?是 `sum(A[:j]) * A[j]`,即到 j 之前的前綴和再乘上 A[j],於是得到 code:
```python
mod = 10 ** 9 + 7
N = int(input())
A = list(map(int, input().split()))
ans = 0
pref = A[0]
for j in range(1, N):
ans = (ans + A[j] * pref % mod) % mod
pref = (pref + A[j]) % mod
print(ans)
```
## D. Friends
答案就是最大的那一群的大小,因為需要同樣數量的組,才能將這一群的人統統分開。實作上用 Disjoint Set 或 DFS 都可以(當然是 Disjoin Set 模版用起來!)
```python
class DisjoinSet:
def __init__(self, N):
self.par = [-1] * N
self.sz = [1] * N
def root(self, x):
if self.par[x] < 0:
return x
else:
self.par[x] = self.root(self.par[x])
return self.par[x]
def unite(self, a, b):
a = self.root(a)
b = self.root(b)
if a == b:
return
if self.sz[a] > self.sz[b]:
a, b = b, a
self.par[a] = b
self.sz[b] += self.sz[a]
def same(self, a, b):
return self.root(a) == self.root(b)
def size(self, x):
return self.sz[self.root(x)]
def __str__(self):
clusters = defaultdict(list)
for x in range(N):
clusters[self.root(x)].append(x)
return str(list(clusters.values()))
N, M = map(int, input().split())
dsu = DisjoinSet(N)
for _ in range(M):
x, y = map(int, input().split())
x, y = x - 1, y - 1
dsu.unite(x, y)
ans = max([dsu.size(x) for x in range(N)])
print(ans)
```
## E. Coprime
判定 not coprime 是簡單的,求出整個序列的 gcd,看值是不是大於 1。難點在於需要高速地求出任兩項是不是互質。即然 A[i] 最大才 10^6,那我們可以對每項都質因數分解,看有沒有某個質數重複出現,因為互質就代表沒有相同的質因數。
例如 [6, 4, 7],因為 6=2x3,我們分解完 4 = 2^2 後發現質數 2 之前有被用過了,那這個序列就不可能是 pairwise coprime。
可以使用 set 來記錄用過了哪些質數。質因數分解時要注意不能對 1 做,把 1 的情況特判掉吧。
pypy 版本跑 384ms, python 版本跑 1225ms, c++ 版本跑 122ms。
```python
import math
class SieveOfEratosthenes:
def __init__(self, V):
self.is_prime = [True] * (V + 1)
self.primes = []
for i in range(2, V + 1):
if self.is_prime[i]:
self.primes.append(i)
for j in range(i * i, V + 1, i):
self.is_prime[j] = False
def factorize(self, x):
assert x > 1
result = []
for p in self.primes:
exp = 0
while x % p == 0:
exp += 1
x = x // p
if exp > 0:
result.append((p, exp))
if p * p > x:
break
if x > 1:
result.append((x, 1))
return result
def solve():
N = int(input())
A = list(map(int, input().split()))
if all(a == 1 for a in A):
return 'pairwise coprime'
gcd_all = 0
for a in A:
gcd_all = math.gcd(gcd_all, a)
if gcd_all > 1:
return 'not coprime'
sieve = SieveOfEratosthenes(max(A))
used_primes = set()
for a in A:
if a != 1:
factors = sieve.factorize(a)
for (p, e) in factors:
if p in used_primes:
return 'setwise coprime'
used_primes.add(p)
return 'pairwise coprime'
print(solve())
```
## F. I hate Shortest Path Problem
等到[官方題解](https://atcoder.jp/contests/abc177/editorial/94)出來後,才搞清楚要怎麼解這題。官方題解寫得很不錯:我們維護 (end_col, start_col) 代表用最少步數到達 `(r, c)` 是從 row 0 哪個 col 出發的,這樣 row r 的答案就是 `min(end_col - start_col for all pairs)` + `r`。前者是我們向右了幾步,後者是向下了幾步。
實作上用 map 存當前 row 的 pairs,再用 multiset 存 end_col - start_col。當我們要遞推至下一行時,假設當前行禁止向下區間為 [a, b],那我們刪除所有 end_col 在 [a, b + 1] 之間的 pairs,然後更新到達 col b + 1 的 start_col。因為當前行中,col 在 [a, b + 1] 之盼的這些格子都會走到下一行的 col b + 1。
對於每個 row,我們迭代了其中要刪除的那些 pairs 再外加一個 b + 1。pairs 的數量會越來越少,整體而言我們迭代過的 pair 數量仍然是 $O(W)$,於是時間複雜度是 $O((H + W)log(W))$
```cpp
#include <iostream>
#include <map>
#include <set>
#include <vector>
using namespace std;
void solve() {
int H, W;
cin >> H >> W;
multiset<int> steps; // num of steps going right, ie, end_col - start_col
map<int, int> end2start; // end col to start col
for (int c = 0; c < W; c++) {
end2start[c] = c;
steps.insert(0);
}
for (int r = 1; r <= H; r++) {
int a, b;
cin >> a >> b;
a--;
b--;
// Erase pairs (e, s) that a <= e <= b + 1
auto lb = end2start.lower_bound(a);
auto ub = end2start.upper_bound(b + 1);
auto max_start = -1;
for (auto it = lb; it != ub; ++it) {
int end = it->first;
int start = it->second;
steps.erase(steps.find(end - start));
max_start = max(max_start, start);
}
end2start.erase(lb, ub);
// Update pair (b + 1, s)
if (b != W - 1 and lb != ub) {
end2start[b + 1] = max_start;
steps.insert(b + 1 - end2start[b + 1]);
}
if (steps.size() > 0) {
cout << *steps.begin() + r << endl;
} else {
cout << "-1" << endl;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}
```
:::success
[< 回到所有題解](https://hackmd.io/@amoshyc/HkWWepaPv/)
:::