−目次
AtCoder Beginner Contest 148 D~F問題メモ
比較的優しい回という印象。つまり不注意によるWAが響く。
D - Brick Break
問題
- 長さ N の数列 A={a1,a2,...,aN}
- 好きな要素を消す操作を何回でもおこなうことができる(消された要素の後ろは前に詰める)
- 消した結果、数列を {1,2,3,...} のように、i 番目の要素が i であるようにしたい
- 消す必要のある最小数を求めよ
- 不可能な場合は
-1
を出力せよ - 1≤N≤2×105
解法
消す要素数を最小化するには、残す要素数を最大化すればいい。
元の A において左から順に 1→2→3→4→… と拾える数列を残すことができる。しかし順番は変えられないので、'1'の前に'2'が出てきても、その'2'は使えない。
'1'がどこにも無ければ不可能なので'-1'を出力して終了。
その他は、先頭から貪欲に残せる要素を拾っていって、残る要素数を N から引いた値が答え。
N = 10 A 3 1 4 1 5 9 2 6 5 3 残す 1 2 3 ↓ 消す 10 - 3 = 7
貪欲に残してよい証明を与える方法の1つとして、貪欲に残さなかった場合によくならないことを示せばよい。
たとえば'3'について、(A)残すことが可能な最初の'3'でなく、(B)その次に出てくる'3'を残すとする。 すると次以降の「'4'→'5'→'6'→…」は'3'の後に出てくる中から選んで残すことになるが、(B)の場合に残すことが可能な要素は全て、(A)の場合でも残すことができる。 残す要素数を最大化するにあたり、貪欲にしないことによって改善することはない。
(A) (B) ... 3 1 4 3 4 2 ... |-------------- ... (A)を残すことで次に残す'4'を採用可能な範囲 |----- ... (B)を残すことで次に残す'4'を採用可能な範囲
実装では、多くのプログラミング言語では、配列内の検索で開始indexを指定できるのでそれを使えばよい。 検索する要素が存在しないかも知れない点に注意。Pythonでは例外処理により対応するのがメジャー?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
n = int ( input ()) aaa = list ( map ( int , input ().split())) if 1 not in aaa: print ( - 1 ) exit() i = 0 a = 1 while True : try : j = aaa.index(a, i) except ValueError: break i = j + 1 a + = 1 print (n - a + 1 ) |
E - Double Factorial
問題
- 0以上の整数 n に対し f(n) を次のように定義する
- f(n)=1 (n=0,1)
- f(n)=nf(n−2) (n≥2)
- 整数 N が与えられる
- f(N) の末尾に何個の0が続くかを求めよ
- 0≤N≤1018
解法
ちょっと書いてみると、f はつまり二重階乗である。
- f(n=偶数)=n までの全ての偶数の積 =n!!
- f(n=奇数)=n までの全ての奇数の積 =n!!
N が奇数の時は、そもそも f(N) が偶数になり得ないので、10の倍数にもなり得ない。'0'が答え。
偶数の時は、掛け合わせる要素の素因数として2が出てくる数と5が出てくる数の少ない方が答えとなる。今回の場合、明らかに5の方が少ない。
- 2a×5b×Xc×...→min(a,b) 個の'0'が並ぶ
よって、「N までの偶数の中に、5の素因数は何個ありますか」という問題である。
偶数という制限が無ければ、このような問題は、N を5で繰り返し割って、その都度、商を足した結果が答えとなる。
N = 600 5を 1個 素因数に持つ数は 600/5 = 120 個 その中で 5をもう 1個 素因数に持つ数は 120/5 = 24 個 その中で 5をさらに1個 素因数に持つ数は 24/5 = 4 個 → 148 個
今回は偶数なので、最初だけ10で割り、後は5で割っていけばよい。
偶数かつ5の倍数 10 20 30 40 50 ... 100 ... 600 10で割った結果 1 2 3 4 5 ... 10 ... 60 → この数列から、さらに5の素因数が残っている数を探す = 制限のない N=60 の問題と見なせる
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def solve(n): if n % 2 = = 1 : return 0 d = n / / 10 ans = d while d: d / / = 5 ans + = d return ans n = int ( input ()) print (solve(n)) |
F - Playing Tag on Tree
問題
- N 頂点の木があり、i 番目の辺は頂点 ai と bi を双方向に結ぶ
- 高橋君と青木君が木の上で鬼ごっこをする
- はじめ、高橋君は頂点 u、青木君は頂点 v にいる(この2頂点は必ず異なる)
- 以下の手順の繰り返しで行う
- 2人が同じ頂点にいたら終了。そうで無ければ高橋君は隣接する頂点を1つ選んで移動
- 2人が同じ頂点にいたら終了。そうで無ければ青木君は隣接する頂点を1つ選んで移動
- 高橋君はできるだけ長くゲームを続け、青木君ははやく終了させたい
- 2人が最適に動いた時、終了するまでに青木君が移動する回数を求めよ
- 2≤N≤105
解法
判定は毎ターン行われるので、すれ違いはできない。
青木君の移動を考えると、常に高橋君のいる方角の頂点を選んで移動した方がよい。 別の道に逸れてしまうと、高橋君から近づかない限り結局引き返さないと捕まえられないが、高橋君はそのような移動をわざわざする意味はない。
高橋君は、奥に逃げられるなら奥に逃げた方がよいので、捕まるのは行き止まりで追い詰められたときである。 その際、なるべく長く逃げられるよう、行き止まりが遠い枝を選んで逃げるのがよい。
まず①が思いつく。 また、少し考えると、②でも可能である。(実質①は②に包含される)
- ①最初から、行き止まりが遠い方へ逃げ続ける
- ②一旦青木君に近づき、ある頂点から行き止まりが遠い方へ逃げ続ける
,-○ 青 ------○----- 高 ----------→ ① | `-○ `-------------------→ ②
どちらを取っても、青木君の移動回数は開始地点から① or ②への距離に近い値になるので、より青木君から遠い頂点で捕まりたい。
①より遠い②があればそちらに逃げ込みたいが、近づいたところで捕まってしまっては元も子もないので、近づけるのは青木君に捕まらない範囲までとなる。
最終的に捕まる頂点
最終的に捕まる状況はどうなるかというと、2通りのパターンがある。
- 青木君が、高橋君のいる頂点に移動する
- 高橋君が、行き止まりで仕方なく青木君のいる頂点に移動する
実際に試してみると、どちらにしても捕まる頂点は「行き止まりの1つ手前の頂点」となる。
これ、「高橋君の移動回数」なら場合分けする必要があったが、「青木君の移動回数」なので、純粋に青木君の初期位置からの距離を答えてよい。
実装
まず、青木君を根として「各頂点の深さ」と「その頂点から最も深い葉までの距離」を計算しておく。
高橋君が、青木君に捕まらない範囲で近づけるぎりぎりの頂点まで近づき、折り返す。 (実際には①の戦略でよかったとしても、この移動の仕方で、捕まる位置は変わらない)
「折り返す頂点の深さ」+「その頂点から最深の葉までの距離」が答えとなる。
折り返せず捕まる最大の頂点の深さは、高橋君の開始位置の深さの 12(切り捨て)となる。
(または、高橋君と青木君の両方を始点としてDFSを2回行い、高橋君の方が速いターンでたどり着ける葉、としてもよい)
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 |
import sys sys.setrecursionlimit( 10 * * 5 ) def dfs(v, parent, depth): parents[v] = parent self_depths[v] = depth sd = 0 for u in links[v]: if u = = parent: continue res = dfs(u, v, depth + 1 ) sd = max (sd, res + 1 ) subtree_depths[v] = sd return sd def solve(u): catch = self_depths[u] / / 2 while self_depths[parents[u]] > catch: u = parents[u] return self_depths[u] + subtree_depths[u] - 1 n, u, v = map ( int , input ().split()) u - = 1 v - = 1 links = [ set () for _ in range (n)] for line in sys.stdin: a, b = map ( int , line.split()) a - = 1 b - = 1 links[a].add(b) links[b].add(a) parents = [ - 1 ] * n self_depths = [ 0 ] * n subtree_depths = [ 0 ] * n dfs(v, - 1 , 0 ) print (solve(u)) |