code festival 2014 エキシビジョン 解説
DESCRIPTION
CODE FESTIVAL 2014 エキシビジョン 解説TRANSCRIPT
CODE FESTIVAL 2014 エキシビジョン解説
AtCoder株式会社代表取締役
高橋直大
2014/11/8 1
©AtCoder Inc. All rights reserved. 2
A問題 パズル
2014/11/8 2
問題概要
• 頂点数N(≦2000),辺数M(≦2000)のグラフが与えられる。きさ3のクリークを3つ組と呼び,3つ組の値を図のように「回転」できる
• 最初,頂点1に番号1,頂点2に番号2,…,頂点Nに番号Nが割り振られている
• 回転を任意の回数繰り返して,与えられる目標の番号付けにできるか?
1
3 2
4
3
2 1
4(1,2,3)を回転したとき
考察
• 3つ組構成辺のみで構成される連結な大きさのグラフで全探索してみると,状態空間は2つに分断されていることが分かる
→偶奇性が存在する
• 上記のグラフにおいて,連結であれば,直接3つ組でなくとも,任意の頂点を3つ組とみなすことができそう→ できる
4
52 1
3
67 8
4
52 1
3
67 8
3つ組構成辺のみで構成されるグラフ(右)
©AtCoder Inc. All rights reserved. 5
B問題 かっこつけ
2014/11/8 5
問題概要
● 開きカッコと閉じカッコからなる文字列Sがある
● 以下「文字列」とはカッコのみからなるものとする
● Sに以下のような操作を合わせてQ回行う
● 開きカッコや閉じカッコを挿入する
● ある文字を削除する
● ある部分文字列「カッコ悪さ」を求める
● 1 ≦ |S| ≦ 10^5
● 1 ≦ Q ≦ 10^5
考察
● 文字列の「カッコ悪さ」とは?
● 文字列からいくつか文字を取り去ってカッコの対応を整合にするときに、取り去る文字の個数の最小値
● どうすれば求めることができる?
考察
● カッコを扱うときに多用される変換
● '(' → +1 ')' → -1
● (())()() → +1, +1, -1, -1, +1, -1, +1, -1
● 累積和を取る
● (())()() → 1, 2, 1, 0, 1, 0, 1, 0
● 変換して累積和をとった後の配列をAとする
考察
● (())()() → 1, 2, 1, 0, 1, 0, 1, 0
● ある文字列Sのカッコの対応が整合であることの必要十分条件は
Aの要素がすべて非負 かつ Aの最後の要素が0
● これは以下のように言い換えることが出来る
Aの最小値が0 かつ Aの最後の要素が0
● 「カッコ悪さ」もAの値から求めることが出来る
考察
● ある文字列Sから閉じカッコを取り除くと、Aのそれ以降の値が+1される
● これによってAの最小値を0に調節することが出来る
● ある文字列Sから開きカッコを取り除くと、Aのそれ以降の値が-1される
● これによってAの最後の値を0にすることが出来る
考察
● この操作によってある文字列Sを整合にすると(Aの最後の値) – 2*(Aの最小値)個の文字を取り去ることになる。
● 実はこれより少ない個数の文字を取り除くことでは文字列を整合にできない
● Aの最後の値とAの最小値を一致させつつ、その値を0にするためにはこれより少なくできない
● よってこの値が「カッコ悪さ」となる
考察
● 部分文字列の「カッコ悪さ」についても同様に考えることが出来る
● 文字列Sのl文字目からr文字目の「カッコ悪さ」は
● A[l] + A[r] – 2*(A[l]~A[r]の中の最小値)
● になる
考察
● よって数列Aに対して以下の操作ができれば良い
● ある範囲に一様に値を足す
● Aのある場所に値を挿入する
● Aのある場所の値を削除する
● ある範囲の値の最小値を求める
● Aのある場所の値の参照
アルゴリズム
● よって数列Aに対して以下の操作ができれば良い
● ある範囲に一様に値を足す
● Aのある場所に値を挿入する
● Aのある場所の値を削除する
● ある範囲の値の最小値を求める
● Aのある場所の値の参照
● これらを高速に行えるデータ構造は?
アルゴリズム
● 平衡二分探索木!!
アルゴリズム
● 平衡二分探索木!!
● 実装が重い
● エンバグしやすい
● 2問1時間のコンテストで組むもの(組めるもの)ではない
● どうすればよいのか?
アルゴリズム
● 平方分割!!
アルゴリズム
● 平方分割!!
● 数列AをB個ずつのバケットに区切って処理する
● 各バケットはその範囲に一様に足されている値を保持する
● 値が挿入、削除されたらそのバケット内で単純に挿入、削除するO(B)
● クエリB回に一回、バケットの各サイズがB
個ずつになるように再構成する
アルゴリズム
● 計算量
● バケットのサイズ
– B回に一度サイズを調整するので、バケットのサイズは0~2Bに収まっている。つまりO(B)
● バケットの個数
– バケットの個数は再構築の時以外かわらない。最大でAの要素数 / B個ある。全クエリが挿入だったとしてもバケットの個数は(N+Q) / B
アルゴリズム
● 計算量
● 挿入、削除、値の参照
– O(バケットの個数)でバケットを探索
– O(バケットのサイズ)でバケット内で挿入,削除,参照
– 合わせてO(B+(N+Q)/B)
● 一様に足す、最小値を求める
– O(バケットの個数 + バケットのサイズ)
– O(B+(N+Q)/B)
● 再構築
– O(N)
– 再構築の回数はN/B回
アルゴリズム
● 全体の計算量
● カッコの挿入、削除(値の挿入、削除、一様に足す)
– O(Q × (B + N/B))
● カッコ悪さを求める(参照、最小値)
– O(Q × (B + N/B))
● 再構築
– O(N/B × N)
アルゴリズム
● まとめると
● O(Q×(B+N/B) + (N^2/B))
● N,Q≦10^5なのでB=sqrt(10^5)≒340くらいにするのが最適
● O(Q×340+N^2/340)
● 間に合う
実装
● バケットのサイズはたかだか2Bで、バケットの個数はたかだか(N+Q)/Bなので動的配列(vector)等をつかわなくとも普通の配列で実装可能です
2014/11/8 24