狠狠撸

狠狠撸Share a Scribd company logo
AtCoder Regular Contest 022
解説
AtCoder株式会社
2014/5/3 1
A問題
スーパーICT高校生
A-問題概要
● 文字列Sが与えられる
● Sからいくつか文字を省いて”ICT”を作れるか
判定せよ
● 大文字小文字は区別しない
1≦∣S∣≦100
A-想定解
● 残す'I'はSの中で一番最初の'I'でよい。
● 残す'C'はその'I'以降で一番最初の'C'でよい。
● 残す'T'はその'C'以降で一番最初の'T'でよい。
● 順番に貪欲的に文字を取っていく。
● O(|S|)
– 想定解
A-想定解2
● 今回は探索する'ICT'が3文字なので、さまざまなその
場しのぎのアルゴリズムが考えられる。
● 3文字を全通り試す
– O(|S|^3)
● 'C'を決めてその左右に'I'や'T'があるか判定
– O(|S|^2)
● 最初の'I'と最後の'T'の間に'C'があるかどうか判定
– O(|S|)
A-大文字小文字
● この問題では大文字小文字を区別しない
● 知るかぎり、どのプログラミング言語でも大文
字と小文字は別物と判断されるので、何とかし
なければならない
– 自前の比較関数を作る
– あらかじめ全て大文字か小文字に揃える
B問題
細長いお菓子
問題概要
? お菓子がうんぬんと言っていますが要するに、
? 長さNの数列Aが与えられるので、同じ数字を2つ
以上含まない最長の区間の長さを求めよ。
? 部分点(50点): N 100、Ai 100
? 部分点(99点): N 1000、Ai 1000
? 満点:N 10^5、Ai 10^5
部分点(50点)N 100
? 区間の左端と右端を全て試し、その区間の中に同じ
数字が2つ以上含まるかどうかを調べる。
→「同じ数字が2つ以上含まれるかどうか」を高速
に判定したい。
? 方法1:頻度を計算しておく
? 「それぞれの数字がいくつ含まれるか」を、配列を
使って求めておき、2つ以上含まれる数字がないか
を調べる。
for (int i = L; i <= R; i++) freq[A[i]]++;
頻度の計算の例
判定のやり方
? 方法2:集合を扱うデータ構造を使う
? 例えばsetやDictionaryなど。
? 集合に数字をどんどん追加していき、すでに追加さ
れている数字があれば、ダメ。
判定のやり方
部分点(99点)N 1000
? 区間の左端だけを全部試し、それぞれに対してどれ
だけ区間をのばすことが出来るかを調べる。
? ひとつずつ数字を見ていき、その数字がすでに区間
の中に入っていたらその時点でストップ、とする。
? 判定の方法は50点の部分点の方法と同様の方法を
使えば良い。(方法1でも方法2でも良い)
満点 N 10^5
? 尺取法を使う。
? 判定の部分に 方法2 を使う場合は、データ構造が
削除クエリに対応している必要があります。
l = 0;
for (r = 0; r < N; r++) {
while ([l,r]に同じ数字が2つ以上含まれている) l++;
answer = max(answer, r - l + 1);
}
尺取法の例
C問題
ロミオとジュリエット
問題概要
? 要するに、
? 頂点数Nの木が与えられるので、距離が最長となる
頂点のペアを1つ見つけよ。
? 部分点(40点):N 1000
? 満点:N 10^5
部分点(40点)N 1000
? 片方の頂点を全て試し、その頂点から最も遠い頂点
を探す。
? 最も遠い頂点を探すためにはBFSを使えば良い。木
なのでDFSを使ってもOK。
? 木の上ではDFSをすることが多いので、DFSで書け
るようになっておくのがオススメです。
満点 N 10^5
? 2頂点間の最大の距離=直径と言います。
? すなわちこの問題は、木の直径を求める問題です。
? 木の直径を求めるアルゴリズムを2通りほど紹介し
ます。
木の直径の求め方その1
? 簡単な木DPをする。
? とりあえず根を適当に決めて根付き木にします。
? 任意の2頂点間の移動は「まず根の方向にいくつか
辿り、根から離れる方向にいくつか辿る」となって
います。
? LCA(Lowest Common Ancestor)
に注目します。
(移動で通る頂点のうち最も根に近い頂点がLCAです。)
4 5
1
6
2 3
87
木の直径の求め方その1
? ある頂点(Vとする)をLCAとする頂点のペアのう
ち、最も距離が離れているものは、
? 「あるVの子の子孫のうち最もVから遠い頂点」(V
の子の数だけある)と「V」のうち、
Vから遠い順に2つ選んだ時の頂点のペア
になります。
? これをボトムアップに(葉から順に)
計算して行けば良い。
4 5
1
6
2 3
87
木の直径の求め方その2
? こちらは、方法としてはとても単純です。
? まず、頂点を適当に選んでWとします。
? Wから最も遠い頂点を探してVとします。
? Vから最も遠い頂点を探してUとします。
? すると、VとUが答えになっています。
木の直径の求め方その2(仕組みについて)
? 木には中心というものがあります。
? 木の中心とは、直径の中心のことです。
→ 中心は辺上の点になることもあります。
? 全ての直径は木の中心を通ります。
→ 中心から各頂点への距離は直径/2以下で
????と考えたりすると証明できます。
V U
W
木の直径の求め方その2(仕組みについて)
? Vは中心をまたいでWと反対側にある最も遠い頂点
であるため、直径の端点となる。
? Uは中心をまたいでVと反対側にある最も遠い頂点
であるため、直径のもう片方の端点となる。
? というからくりになっています。
V U
W
まとめ
? 1つ目の方法は汎用性が高いテクニックなので、書
けるようになっておいた方が良いでしょう。
? 2つ目の方法は、直径を求める際には楽な方法なの
で、覚えておいて損は無いでしょう。
D問題
スプリンクラー
D-問題概要
● 直交座標上にN個の格子点(x, y)が与えられる
● 各格子点に対して、それを中心として原点を通
る円を考える
● いずれかの円に含まれる格子点の個数を求めよ
● N ≦ 10^5
● -10^5 ≦ x, y ≦ 10^5
D-部分点1
● N ≦ 100
● -100 ≦ x, y ≦ 100
● 頂点と円の組み合わせを全て調べても間に合う
● O(N |x| |y|)
– 10点が得られる
D-部分点2
● N ≦ 1,000
● -1,000 ≦ x, y ≦ 1,000
● 全頂点の走査なら可能そう
● いろいろな図形を重ねた時に、何枚重なってい
るか数える都合のいいアルゴリズムがあった気
がする???
D-部分点2
累積和
D-部分点2
● 長方形ではないのでいつもの二次元累積和は使
えない
– 一次元に分けたらできる
● 円周の左側を+1 右側を-1する
D-部分点2
● +1や-1する点は円1つあたり、たかだかO(半
径)個くらい
– 今回なら 1000 * √2
● 総計算量はO(N max(|x|, |y|))
– 計30点が得られる
D-考察
● 累積和をすると、そこにいくつの円が重なって
いるかという余計な情報も求めることになる
– 無駄
● 1個以上重なっているかどうかさえわかればい
い
– 無駄な+1, -1をしないように削る
D-考察
● 無駄な+1, -1の例
D-考察
● どうやら完全に外側に接している部分付近だけ
の+1, -1で充分
● 外周さえ求まれば
なんとかなりそう
● どうすれば
求まるのか???
D-部分点2
凸包
D-考察
● 点A,点Bと三角形OAB内の点C
● Cを中心とする円はAもしくはBを中心とする円
に完全に覆われる
● 颁は考えなくて良い
D-考察 部分点3
● どの三角形の内側にも入らない点だけを考えれ
ば良い
– つまり凸包上にある頂点だけを考える
● 頂点は全て格子点にあるので凸包上にある点は
たかだか√|x|個くらい
● 凸包で頂点を減らしてimos法をすれば良い
– O(x log x + x√|x|) 計60点が得られる
D-さらに処理を少なく
● 凸包上の点を中心とする円でも、その円周が全
て外側に触れている(外周)わけではない
– 外周のところだけで計算してよい
● 円どうしの交点、つまり円弧の両端が求まれば
良い
● 交差する円は、凸包の多角形における隣り合う
頂点に対応する円だけ
– 幾何の計算をいくらかすれば交点はもとまる
D-外周だけを考える
● 各円弧の両端が求まれば、その間に含まれる範
囲だけ考えれば良い。
– ある点がある円弧に含まれるかどうかは凸包と同様
に外積を使えば良い
● 外周だけ考えるのは何とかなりそうだけれども
はたしてそれで満点の制約において間に合うの
か?
D-計算量
● 累積和において別々に考えるときに、y=k(k
は整数)となる直線で輪切りにして考えるとす
る。
● そのとき、各直線上には左端と右端がいくつか
乗る。それを計算すれば各直線上の条件に合う
花の数が求まる。
D-計算量
● 右端や左端は外周がy=k(kは整数)と交わる点
の数だけある。
● 外周を一周りするときにy=k(kは整数)と何回
交わるかを求めれば、計算量が求まる。
D-計算量
● 各円弧の両端と原点を結んだ三角形の原点の部
分の内角は、その円弧の円周角に当たる。
– 全外周の円周角の総和はたかだか360°
– よって中心角の総和はたかだか720°
● 円の半径は|x|*√2以下なので、外周の上限は
|x|*4√2 * (円周率)
● 今回の問題ではおおよそ2,000,000くらい
D-計算量
● 外周をたどって一回りする時、いろいろなy=k
と交わる。
● いまy=aと交わったとする。その次に交わるの
はy=a+1, y=a-1, y=aのいずれかである
D-計算量
● 次にy=a+1,y=a-1と交わることは、外周を1
以上進んでいるので2,000,000回しか起こらな
い
● 次にy=aと交わることは、下図のような場合の
み。共にO(N)回
接線がx軸と平行になる付近 違う円弧と変わる付近
D-計算量
● よって、外周とy=k(kは整数)との交点はたか
だか2,000,000個くらいしかないので全列挙し
ても十分間に合う
– 想定解
D-想定解まとめ
● スプリンクラーと世界一の花で凸包を取る。
● 凸包上の点とそれに対応する円を順番に見てい
き、隣の円との交点を求め、外周となる範囲を
得る
● 外周上を順番に走査してy=k(kは整数)との交
点を全て列挙する
● 左端と右端が全て求まるので適当に各yについ
て引き算をして、総和を取ると答えになる
D-計算量まとめ
● 凸包でO(N log N)
– バケツソートによる基数ソートをすればO(N)
– 凸包上の点の数をMとする M = O(√|x|)
● 円どうしの交点を求めるのにO(M)
● y=k(kは整数)との交点を全列挙するのにO(|x|
*4√2 * 円周率)
● おおよそ10^6オーダー

More Related Content

AtCoder Regular Contest 022 解説