目次
AtCoder Beginner Contest 120 B,C,D問題メモ
B - K-th Common Divisor
問題
- 正整数 $A,B$ を共に割り切る、$K$ 番目に大きな正整数を求めよ
- $1 \le A,B \le 100$
解法
もんだいぶん を よく よみましょう(大きい方から $K$ 番目)
解法は単純に全探索でよい。
a, b, k = map(int, input().split()) t = 0 for i in range(100, 0, -1): if a % i == 0 and b % i == 0: t += 1 if t == k: print(i) break
制約が大きくなっても、「公約数は最大公約数の約数」なので、まずGCDを見つけてから約数を求めると、$O(\sqrt{\min(A,B)})$ くらいで解ける。
C - Unification
問題
- '0'と'1'のみからなる文字列 $S$ がある
- 以下の操作を好きな回数、好きな箇所に適用できる
- '01'または'10'を消す
- 消されてできた隙間を詰める
- 上手く操作をすると、最大何文字消すことが出来るか
解法
最終状態(これ以上消せない状態)がどんなものか考える。
'0'と'1'が両方残っているのにこれ以上消せない状態があるとする。そのような状態は、どこかで'0'と'1'が隣り合っている。ならばそこで消せることになる。
よって矛盾するので、これ以上消せない状態は、'0'か'1'のどちらかは残っていない状態であることが言える。
1回につき'0'と'1'を1個ずつ消すので、最大'0'と'1'の個数の少ない方だけ消すことが出来る。
s = input() _0 = s.count('0') _1 = s.count('1') print(min(_0, _1) * 2)
D - Decayed Bridges
問題
- $N$ 個の島を、$M$ 本の橋が結んでいる
- 橋には $1,2,...,M$ の番号が付き、$i$ 番目の橋は、島 $A_i$ と $B_i$ を結んでいる
- ところが、橋は番号が小さい方から順に崩落していく
- $1 \le i \le M$ について、$i$ 番目の橋が崩落した直後の以下の個数を求めよ
- 互いに行き来することができない島のペアの数
- 順序を入れ替えただけのものは1つと数える
- $2 \le N \le 10^5$
- $1 \le M \le 10^5$
解法
$i$ 番目の橋が落とされた時、いくつの島のペアが行き来不能になったのか? というのを時系列通りに考えるのは、ちょっとむずい。 探索すれば答えは出るが、制約上、崩落ごとに探索を繰り返していたのでは時間がかかってしまう。
ここはUnionFind木を使って、時系列を逆方向に辿っていくのがよい。分割の問題を結合にすり換える。
全ての孤立した島を、徐々に橋を建設して繋いでいくイメージとなる。 UnionFindを使うと、橋によって行き来可能になる島のペア数の計算がしやすくなる。
最初は、全ての島が孤立しているので $\frac{N(N-1)}{2}$ 個のペアが行き来不能。
そこから遡って $i$ 番目の橋を建設する時は、以下の操作によりペア数を求められる
$A_i$ と $B_i$ が、$i$ 番目の橋を建設前から行き来できていた
$i$ 番目の橋が出来ても、行き来できないペアは変わらない。
$A_i$ と $B_i$ は、$i$ 番目の橋ができて始めて行き来できるようになった
$A_i$ と $B_i$ をUnionする。
「$A_i$ 側に属する島数 × $B_i$ 側に属する島数」だけ、行き来できる島のペアが増えることになる。つまり、行き来できない島のペアが減る。
実装
UnionFindを使いながらも、一工夫が必要。「自身のグループに属する要素の数」を、保持して更新しなければならない。
これは、UnionFind上で親の番号を保持する配列(下記table
)で、根要素に自身のグループの要素数を持たせるなどで実装できる。
その際、自身が根であることを区別できるように、要素数は負数で持たせる。
table[i]
が正ならその値は $i$ の親の番号、負なら $i$ は根で絶対値はグループの要素数を示すようにする。
もちろん、そうせずとも要素数保存用配列を別に用意してもよい。消費メモリが多少増えるだけで、1つの配列に2つの意味を持たせるよりはわかりやすいコードになる。
要素数を持っておくと、Union時に要素数の少ない方を多い方に繋ぐようにすると、木の平衡状態を保つのにも役立つ。
import sys class UnionFind: def __init__(self, n): self.table = [-1] * n def _root(self, x): if self.table[x] < 0: return x else: self.table[x] = self._root(self.table[x]) return self.table[x] def find(self, x, y): return self._root(x) == self._root(y) def union(self, x, y): r1 = self._root(x) r2 = self._root(y) if r1 == r2: return d1 = self.table[r1] d2 = self.table[r2] if d1 <= d2: self.table[r2] = r1 self.table[r1] += d2 else: self.table[r1] = r2 self.table[r2] += d1 n, m = map(int, input().split()) bridges = list(sys.stdin) bridges.reverse() buf = [n * (n - 1) // 2] uft = UnionFind(n) for line in bridges[:-1]: a, b = map(int, line.split()) a -= 1 b -= 1 if uft.find(a, b): buf.append(buf[-1]) continue ra = uft.table[uft._root(a)] rb = uft.table[uft._root(b)] buf.append(buf[-1] - ra * rb) uft.union(a, b) buf.reverse() print('\n'.join(map(str, buf)))