−目次
AtCoder Beginner Contest 120 B,C,D問題メモ
B - K-th Common Divisor
問題
- 正整数 A,B を共に割り切る、K 番目に大きな正整数を求めよ
- 1≤A,B≤100
解法
もんだいぶん を よく よみましょう(大きい方から K 番目)
解法は単純に全探索でよい。
1 2 3 4 5 6 7 8 |
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(√min(A,B)) くらいで解ける。
C - Unification
問題
- '0'と'1'のみからなる文字列 S がある
- 以下の操作を好きな回数、好きな箇所に適用できる
- '01'または'10'を消す
- 消されてできた隙間を詰める
- 上手く操作をすると、最大何文字消すことが出来るか
解法
最終状態(これ以上消せない状態)がどんなものか考える。
'0'と'1'が両方残っているのにこれ以上消せない状態があるとする。そのような状態は、どこかで'0'と'1'が隣り合っている。ならばそこで消せることになる。
よって矛盾するので、これ以上消せない状態は、'0'か'1'のどちらかは残っていない状態であることが言える。
1回につき'0'と'1'を1個ずつ消すので、最大'0'と'1'の個数の少ない方だけ消すことが出来る。
1 2 3 4 |
s = input () _0 = s.count( '0' ) _1 = s.count( '1' ) print ( min (_0, _1) * 2 ) |
D - Decayed Bridges
問題
- N 個の島を、M 本の橋が結んでいる
- 橋には 1,2,...,M の番号が付き、i 番目の橋は、島 Ai と Bi を結んでいる
- ところが、橋は番号が小さい方から順に崩落していく
- 1≤i≤M について、i 番目の橋が崩落した直後の以下の個数を求めよ
- 互いに行き来することができない島のペアの数
- 順序を入れ替えただけのものは1つと数える
- 2≤N≤105
- 1≤M≤105
解法
i 番目の橋が落とされた時、いくつの島のペアが行き来不能になったのか? というのを時系列通りに考えるのは、ちょっとむずい。 探索すれば答えは出るが、制約上、崩落ごとに探索を繰り返していたのでは時間がかかってしまう。
ここはUnionFind木を使って、時系列を逆方向に辿っていくのがよい。分割の問題を結合にすり換える。
全ての孤立した島を、徐々に橋を建設して繋いでいくイメージとなる。 UnionFindを使うと、橋によって行き来可能になる島のペア数の計算がしやすくなる。
最初は、全ての島が孤立しているので N(N−1)2 個のペアが行き来不能。
そこから遡って i 番目の橋を建設する時は、以下の操作によりペア数を求められる
Ai と Bi が、i 番目の橋を建設前から行き来できていた
i 番目の橋が出来ても、行き来できないペアは変わらない。
Ai と Bi は、i 番目の橋ができて始めて行き来できるようになった
Ai と Bi をUnionする。
「Ai 側に属する島数 × Bi 側に属する島数」だけ、行き来できる島のペアが増えることになる。つまり、行き来できない島のペアが減る。
実装
UnionFindを使いながらも、一工夫が必要。「自身のグループに属する要素の数」を、保持して更新しなければならない。
これは、UnionFind上で親の番号を保持する配列(下記table
)で、根要素に自身のグループの要素数を持たせるなどで実装できる。
その際、自身が根であることを区別できるように、要素数は負数で持たせる。
table[i]
が正ならその値は i の親の番号、負なら i は根で絶対値はグループの要素数を示すようにする。
もちろん、そうせずとも要素数保存用配列を別に用意してもよい。消費メモリが多少増えるだけで、1つの配列に2つの意味を持たせるよりはわかりやすいコードになる。
要素数を持っておくと、Union時に要素数の少ない方を多い方に繋ぐようにすると、木の平衡状態を保つのにも役立つ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
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))) |