狠狠撸

狠狠撸Share a Scribd company logo
?AtCoder Inc. All rights reserved. 1
DISCO presents ディスカバリーチャンネル プログラ
ミングコンテスト2016 本選 解説
AtCoder株式会社
A: DISCO presents
ディスカバリーチャンネル
プログラミングコンテスト 2016 Ⅱ
問題概要と解法
? N 人が参加したコンテストの順位表をもとに
それぞれの参加者が獲得した賞金を求めよ
? 順位表の情報に従って賞金を決定すればよい
– 実際には賞金が存在するのは 5 位までなので、そこまで
見ればよい
B: DDPC特別ビュッフェⅡ
問題概要
? 時間の経過とともに料理がなくなるビュッフェがある
– 料理にはそれぞれ美味しさと取ることができる締切がある
? 単位時間あたり 1 つ料理を取ることができる
? 取った料理の美味しさの総和が X 以上となる
最小の時刻 t を求めよ
考察
? 美味しい料理から貪欲に取るのがよさそう
– 締切を考慮しなくてはならないので難しい
? 時刻に関して逆に考える
– 時刻の経過とともに料理が追加されるビュッフェになる
– そのとき食べられる最も美味しい料理から取ればよい
? ある時刻 t から料理を取り始めるとき,取った料理の
美味しさの総和が X 以上となる最大の時刻 t を求めよ
という問題になった
想定解法
? 時刻 t に関して二分探索+貪欲
– 優先度付きキューを用いることで料理の追加と削除、
最も美味しい料理の取得が効率よく可能
? 計算量は O(N log2N)
別解
? 賢い貪欲法を行うことでより効率的に解ける
? 最も美味しい料理から貪欲に取っていく
– その際できる限り締切に近い時刻に取ることにしておく
– 時刻 0 でもその料理を取ることができない場合は諦める
? 今取っている他の料理と交換しても,得することは存在しない
– 取った料理の美味しさの総和が X 以上になったとき,
取った料理の個数が答えとなる
? 締切に近い時刻に取ったことにしたのを前倒しにする
? 計算量は O(N logN)
– 他にも様々な解法が存在します
C: 特別講演
「括弧列と色塗り」
問題概要
? “(“と”)”だけからなる対応の取れた括弧列 S 中の
文字を赤と青に塗り分ける
? i 文字目と j 文字目の括弧が対応しているとき、
S[i, j] に含まれる赤色の文字の数 R と青色の文字の
数 B について、|R – B| ≦ K を満たす
? 上記の制約を満たす色の塗り分け方は何通りあるか?
考察
? 対応の取れた括弧列は順序木と対応している
– 正確にはこの問題においては順序木の集合になる
? 「(()(()))」は以下のような順序木に対応
2 3
1
4
考察
? 塗り分けるという操作は 1 つの頂点に赤い石か
青い石を合計 2 個になるように置くことに対応
2 3
1
4
考察
? 塗り分けるという操作は 1 つの頂点に赤い石か
青い石を合計 2 個になるように置くことに対応
? 塗り分けに関する制約は各頂点を根とする部分木に
存在する赤い石の個数を R、青い石の個数を B として
|R – B| ≦ K を満たすという制約になる
2 3
1
4
問題
? 根付き木の集合が与えられる
? 各木のそれぞれの頂点に赤い石と青い石を合計 2 個に
なるように置く
? 各頂点を根とする部分木それぞれについて、部分木に
含まれる赤い石の個数を R、青い石の個数を B として
|R – B| ≦ K を満たす石の置き方は何通りあるか?
解法
? 根付き木どうしの間に石の置き方の制約はない
– 各根付き木についての答えを最後に掛けあわせればよい
? 各根付き木内で
dp(i, r) = 頂点 i を根とする部分木に含まれる赤い石の個数が
r であるような制約を満たす石の置き方の総数
としてDPを行う
? 各頂点において、r を部分木に含まれる頂点の 2 倍
まで見るように遷移を行うと頂点数を N として
計算量は O(N2)となる
D: ディスコ社内ツアーⅡ
問題概要
? N 頂点 M 本の有向辺からなる単純有向グラフが
与えられる
? 各辺にはOかEのラベルがついている
? ある頂点 s から出発して,以下の条件を満たすように
頂点 t に移動したい
– Oのラベルがついた辺をそれぞれ奇数回通った
– Eのラベルがついた辺をそれぞれ偶数回通った
? 始点 s と終点 t の組み合わせはいくつあるか?
考察: 有向オイラー路
? 具体的な辺を通る回数が与えられたときを考える
? これは有向オイラー路の判定問題に近い
– 有向オイラー路とはグラフの辺をちょうど 1 度ずつ通る
ような路(トレイル)のこと
– k 回辺を通るのは,k 本辺があるとすればよい
? 有向オイラー路が存在する条件は
– 無向基礎グラフが連結
– 各頂点の相対出次数が 0 、あるいは相対出次数が 1、– 1 の
頂点が 1 つずつ存在し、他の頂点の相対出次数が 0
? 前者は有向オイラー閉路,後者は有向オイラー路
考察:
? ラベルがOの辺が存在しないならば答えは N
– 以後はラベルがOの辺が少なくとも 1 つあることを仮定
? ラベルがOの辺はそれぞれ一度通ったことにする
– 通る回数を 2 ずつ増加させることで,有向オイラー路が
存在するようにすることが可能か?という問題になる
? それぞれの頂点の相対出次数の偶奇が定まる
– 全ての頂点の相対出次数が偶数
– 相対出次数が奇数であるような頂点が 2 つのみ存在
の 2 つの場合を以後は考える
解法: 全ての頂点の相対出次数が偶数
? 有向オイラー閉路が存在するように辺の通る回数を割当てが可能な条件は
– ラベルがOの辺全てがある強連結成分内に含まれる
であり、このときの答えはそのような強連結成分に含まれる頂点の数
– ある強連結成分に含まれる頂点 u, v を選び、「u の相対出次数に 2 加算し、
v の相対出次数に -2 加算する」という操作が可能である
? u から v への辺があるならばそのような操作が可能なことは自明
? u から v へのパスを適当に選んで通る回数を 2 ずつ増加させることで上記の操作が
可能であることが示せる
– この操作を適切に行うことで、全ての頂点の相対出次数を 0 にすることが可能
– 上記より、ラベルがOの辺全てがある強連結成分内に含まれているときは
そのような閉路が存在することが示せた
– ラベルがOの辺全てがある強連結成分内に含まれていないときは、
ラベルがOの辺全てを通って、元の出発点に戻ることができない
解法: 相対出次数が奇数の頂点が 2 つ存在
? 有向オイラー路が存在するように辺の通る回数を割当てが可能な条件は
– 強連結成分分解により縮約されたグラフにおいて、以下のようなパスが存在
? 縮約された頂点内部にラベルがOの辺が含まれるような頂点たち全てがパスに含まれる
? パスにおいて使用した辺のラベルは全てO
? 縮約されたグラフにおいて、このパスに使用されないラベルがOの辺は存在しない
であり、パスに含まれる頂点の数が 1 ならば答えは 2 、それ以外のときの
答えは 1
– 縮約された頂点内において、全ての頂点の相対出次数を(始点と終点を除いて)
0 にすることが、先程と同様に示される
– パスにおいて使用した辺のラベルがEのとき、 2 回以上通らなくてはならない
ため条件を満たさない
– 縮約されたグラフにおいて、このパスに使用されないラベルがOの辺が
存在するとき、明らかに条件を満たさない
E: アメージングな二分探索木は、
きみが作る!
問題概要
? はじめ,頂点が存在しないような二分探索木 T がある
? 以下の Q 個のクエリを順番に処理せよ
1. 値が v であるようなノードを T に挿入せよ
2. 値が v であるようなノードの左部分木の高さ h(l) と
右部分木の高さ h(r) の差を求めよ
3. 値が v であるようなノードを右回転せよ
左の子が存在しない場合は – 1 を出力せよ
4. 値が v であるようなノードを左回転せよ
右の子が存在しない場合は – 1 を出力せよ
部分点1(20点)
? この問題は平衡しているとは限らない二分探索木の
構築とそのバランスを調べる問題
? 部分点 1 においてはクエリの数が少ないので、
各操作を愚直にシミュレーションすればよい
– 回転操作により根が切り替わる場合があることに注意
部分点2 (30点)
? クエリの種類は 1 と 2 のみ
? 挿入操作、バランスを調べる操作を高速に行いたい
? 挿入操作は、以下のようにして O(logN) で実現可能
– T に含まれるノードの値はそれぞれ異なる
– 値 v のノードの親となるノードは以下のどちらかであり、挿入が
可能なノードは以下のどちらかのうち必ず片方のみである
(ただし、値が v となるノードが根となる場合のみ例外)
? T に含まれるノードのうち、値がv 未満で最大のノード
? T に含まれるノードのうち、値がv より大きく最小のノード
– 上記の親となるノードを調べる操作はsetを使って実現可能
部分点2 (30点)
? バランスを調べる操作を高速に行いたい
– ここで、最終的な木の形は定まっており、回転操作により
途中で木の形が変形することがないことに着目する
– クエリを先読みし、最終的な木を構築する
4
2 6
71 3 5
8
部分点2 (30点)
? 最終的な木を構築し、オイラーツアーを行う
? バランスを調べるクエリはオイラーツアーで割り振った
インデックスを利用してRMQで取得
? 挿入操作により頂点が追加されるたびに、親の情報を
もとにRMQを更新してやればよい
– 更新も取得もセグメント木ならば O(logN) で処理可能
– 更新回数も取得回数もO(QlogQ)で抑えられる
満点 (150点)
? 木の形が回転操作により変わりうる
– オイラーツアーではそのような状況に対応できない
? 与えられる木が二分探索木であることに着目する
– 各ノードに部分木の値の最小値 min と最大値 maxを持たせると
[min, max]が部分木を表す区間になる
– この区間が分かれば、先ほどと同様RMQを用いて解くことが可能
ノードの挿入操作
? 担当する区間は親が担当する区間より一意に定まる
? 挿入されたノードが担当する区間に 1 を加算
– (存在しないノードも親と同じ高さになるが関係ない)
v
[min, max]
l
[min, v - 1]
?
[v + 1, max]
v
[min, max]
l
[min, v - 1]
r
[v + 1, max]
+1
ノードの回転操作
? 担当する区間の変更として表せる
? 2 つの区間に+ 1と-1を加算
v
[min, max]
l
[min, v - 1]
r
[v + 1, max]
v
[l + 1, max]
l
[min, max]
r
[v + 1, max]
+1-1
解法まとめ
? T に含まれている値をsetにより管理する
? 各ノードには、親、左の子、右の子、担当する区間
を持たせる
? 深さは区間加算、区間の最大値の取得が可能な
データ構造(セグメント木など)により管理
? 各クエリを適切に処理する
? これはO(QlogQ)で実現可能

More Related Content

Disco Presents ディスカバリーチャンネルプログラミングコンテスト2016 本選 解説