ABC #027 解説
解説スライド担当 : @sugim48
問題 A – 長方形
問題概要
• ある長方形(正方形も含む)の 3 つの辺の長さが与えられる。残り 1
つの辺の長さを求めよ。
3 ?
4
4
5 ?
5
5
考察
• 向かい合う辺の長さは等しい。
3 ?
4
4
5 ?
5
5
解法
• 与えられた 3 つの数のうち、等しい組を見つけたら、余った数がその
まま答えになる。
4 3 4
𝑎𝑛𝑠
=
5 5 5
=
𝑎𝑛𝑠
解答例 (C++)
int x, y, z;
cin >> x >> y >> z;
int ans;
if (x == y) ans = z;
if (y == z) ans = x;
if (z == x) ans = y;
cout << ans << endl;
問題 B – 島と橋
問題概要
• 𝑁 個の島が横一列に並んでいる。左から 𝑖 番目の島には 𝑎𝑖 人の住
人が住んでいる。
• 隣り合う島の間に橋を架け、住人を移動させることができる。
• すべての島に同じ人数の住人が住むようにできるか? また、最小で
何本の橋を架ければよいか?
• 2 ≤ 𝑁 ≤ 100
• 0 ≤ 𝑎𝑖 ≤ 100
例
例
例
解法
• まず、 𝑖=1
𝑁
𝑎𝑖 が 𝑁 で割り切れなければ、不可能である。
• 割り切れるならば、それぞれの島に
1
𝑁 𝑖=1
𝑁
𝑎𝑖 人ずつ住むことになる。
解法
• 隣り合う島の間ごとに橋が必要か判定していく。
• 下図の橋は必要か? 橋の左側が 1 人、橋の右側が 4 人になって
ほしいので、橋を左から右へ 1 人渡ることになる。
→ 必要
解法
• 下図の橋は必要か? 橋の左側が 2 人、橋の右側が 3 人になって
ほしいが、はじめからそうなっている。
→ 必要ではない
解法
• このようにして、隣り合う島の間ごとに橋が必要か判定し、必要と判
定された橋の本数を答えればよい。
問題 C – 倍々ゲーム
問題概要
• A と B が二人ゲームで勝負する。
• 自然数 𝑁 が与えられる。𝑥 = 1 に初期化する。
• A → B → A → … の順に次の操作を行う。
 𝑥 を 2𝑥 または 2𝑥 + 1 に置き換える。
• 𝑥 > 𝑁 にした人が負け。
• どちらかが勝つか求めよ。
• 1 ≤ 𝑁 ≤ 1018
• A の操作を赤、B の操作を青で表すと、図のように 𝑥 が変化する。
1098 131211 1514
4 5 6 7
2 3
1
• 例えば 𝑁 = 5 のとき、OK の整数と NG の整数はこのように分離され
る。
1098 131211 1514
4 5 6 7
2 3
1
• 境界線をまたぐ操作だけに注目すると、A の操作は左に、B の操作
は右に偏っている。
1098 131211 1514
4 5 6 7
2 3
1
• 境界線をまたぐと負けてしまうので、A はできるだけ右に、B はできる
だけ左に行きたがることが分かる。
1098 131211 1514
4 5 6 7
2 3
1
• これを実際にシミュレートすると B が負けると判定できる。
1098 131211 1514
4 5 6 7
2 3
1
• 別の例として 𝑁 = 10 のとき、OK の整数と NG の整数はこのように
分離される。
1098 131211 1514
4 5 6 7
2 3
1
• 境界線をまたぐ操作だけに注目すると、A の操作は右に、B の操作
は左に偏っている。
1098 131211 1514
4 5 6 7
2 3
1
• 境界線をまたぐと負けてしまうので、A はできるだけ左に、B はできる
だけ右に行きたがることが分かる。
1098 131211 1514
4 5 6 7
2 3
1
• これを実際にシミュレートすると B が負けると判定できる。
1098 131211 1514
4 5 6 7
2 3
1
解法
• 𝑁 の深さの偶奇に応じて、A と B の戦略が決まる。
• A と B の戦略を実際にシミュレートして、どちらかが勝つか判定する。
• 𝑁 の深さは次のようにして 𝑂(𝑙𝑜𝑔𝑁) 時間で計算できる。
int depth = 0;
for (long long n = N; n > 0; n /= 2)
depth++;
問題 D – ロボット
問題概要
• 数直線の原点にロボットが置かれている。はじめ、ロボットの幸福度
は 0 である。
• このロボットが命令列 𝑆 を順に実行する。
 M : 正か負の向きに距離 1 だけ移動する。
 + : 今の座標を 𝑥 とすると、幸福度が +𝑥 だけ変化する。
 - : 今の座標を 𝑥 とすると、幸福度が -𝑥 だけ変化する。
• 最終的にロボットは原点に戻っていなければならない。
• 最終的な幸福度の最大値を求めよ。
• 1 ≤ 𝑆 ≤ 105
部分点解法
• 1 ≤ 𝑆 ≤ 1,000 と小さい。
→ 動的計画法
• 𝑑𝑝 何文字目 座標 ≔ (幸福度の最大値) を埋めていく。
• 𝑑𝑝 |𝑆| 0 が答え。
• 𝑂( 𝑆 2) で間に合う。
満点解法
• 1 ≤ 𝑆 ≤ 105
と大きいので、動的計画法では間に合わない。
→ もっと速い解法を考える。
• ロボットの正の向きへの移動を > 、負の向きへの移動を < と表すこ
とにする。
考察
• >+<<-> という命令列を考える。
• 幸福度の変化量は
 + ごとに (自分より左の > の個数) - (自分より左の < の個数)
 - ごとに (自分より左の < の個数) - (自分より左の > の個数)
• 見方を変えると
 > ごとに (自分より右の + の個数) - (自分より右の - の個数)
 < ごとに (自分より右の - の個数) - (自分より右の + の個数)
考察
• (自分より右の + の個数) と (自分より右の - の個数) が分かれば、
> または < を選んだときの幸福度の変化量を予言できる!
• 例) M--M-M+M+
M - - M - M + M +
> −1 +1 +2 +1
< +1 −1 −2 −1
考察
• 最終的な幸福度を最大化したいので、幸福度が増える向きを貪欲に
選んでいけばいいか?
→ 「最終的にロボットは原点に戻っていなければならない」という条件
を守れない。
M - - M - M + M +
> −1 +1 +2 +1
< +1 −1 −2 −1
考察
• 最終的にロボットが原点に戻るためには、> と < を同じ回数だけ選ば
なければならない。
• この制約下でできるだけ大きいものを選びたい。
M - - M - M + M +
> −1 +1 +2 +1
< +1 −1 −2 −1
考察
• この行を昇順にソートすると → [−1, +1, +1, +2]
• 前半分を < に、後ろ半分を > に割り当てる → −1, +1, +1, +2
• +1 + +2 − −1 + +1 = 3 が答え!
M - - M - M + M +
> −1 +1 +2 +1
< +1 −1 −2 −1
解法
• 命令列 𝑆 の各 M について、
(自分より右の + の個数) - (自分より右の - の個数) を計算し、
配列 𝐴 に格納する。
• 𝐴 を昇順にソートする。
• (𝐴 の後ろ半分の総和) – (𝐴 の前半分の総和) が答え。
• 𝑂( 𝑆 log 𝑆 ) で間に合う。
• なお、バケツソートを用いると 𝑂( 𝑆 )

abc027