40
2015.3.31.TUE.11:00-19:30

Indeedなう B日程 解説

Embed Size (px)

Citation preview

Page 1: Indeedなう B日程 解説

2015.3.31.TUE.11:00-19:30

Page 2: Indeedなう B日程 解説

2

A: Counting on a Triangle

Page 3: Indeedなう B日程 解説

3

●問題概要

• 上記のように,正三角形状に座標が割り振られている(無限に 広がっている)

• 座標(𝑥, 𝑦)の重み = 𝑥 ∗ 𝑦と定義 • A段目からB段目の範囲に含まれる座標の重みの総和を出力 • 答えは大きくなるので109 + 7で余りを取ってください • 1 ≦ 𝐴 ≦ 𝐵 ≦ 106

(1,1) (2,1)(2,2)

(3,1)(3,2)(3,3) (4,1)(4,2)(4,3)(4,4)

:

Page 4: Indeedなう B日程 解説

4

●部分点解法(20点)

• A段目からB段目の間に含まれる全ての座標を列挙 → 座標は 𝐴 + (𝐴 + 1) + … 𝐵 個ある

• 特に最悪ケース(𝐴 = 1)では,1 + 2 + ⋯ + 𝐵 =𝐵 𝐵+1

2 個ある

• 部分点制約(1 ≦ 𝐴 ≦ 𝐵 ≦ 2000)なら間に合う

(1,1) (2,1)(2,2)

(3,1)(3,2)(3,3) (4,1)(4,2)(4,3)(4,4)

:

Page 5: Indeedなう B日程 解説

5

●考察

• ↑の正三角形を積の形にして式を眺めてみる(次スライド)

(1,1) (2,1)(2,2)

(3,1)(3,2)(3,3) (4,1)(4,2)(4,3)(4,4)

:

Page 6: Indeedなう B日程 解説

6

●考察

• 積の形にしてみた(足し算記号は省略)

1×1 2×1 2×2

3×1 3×2 3×3 4×1 4×2 4×3 4×4

Page 7: Indeedなう B日程 解説

7

●考察

• くくってみた • (k段目の座標の重みの和) = k×(1+…+k) • ここまで分かれば,高々100万回ループ回せばよさそう

1×(1) 2×(1+2)

3×(1+2+3) 4×(1+2+3+4)

Page 8: Indeedなう B日程 解説

8

●解法

• [解法1]1からkの総和をナイーブに計算しつつ, A≦k≦Bの範囲で,総和にkをかけたものを答えに足す for文を書く

• [解法2]k=A..Bの範囲で𝑘 ×𝑘(𝑘+1)

2を足すfor文を書く

(総和の公式を使っている 余りを取るタイミングに注意)

• 時間計算量はどちらも O(B)

• [解法3]1段目からN段目までの総和S(N)の公式は 頑張ったら計算できるのでそれを用いてS(B)-S(A-1)を出力. 今回は必要ない.

• 時間計算量は O(1)

Page 9: Indeedなう B日程 解説

9

●ところで

• 今回,人によっては式に𝑘(𝑘+1)

2 や 𝑘 ×

𝑘(𝑘+1)

2が出てきた人が

いると思います.

• これらの計算結果はkが大きいと32bit整数型から溢れます.

• 64bit整数型を用いましょう.

• たとえばC++なら符号付き64bit整数型としてlong long という型が用意されています.

Page 10: Indeedなう B日程 解説

10

B: How are you?

Page 11: Indeedなう B日程 解説

11

●問題概要

• N人の社員がいる

• 社員 i は時刻 Si に出社し、時刻 Ti に退社する

• 自分がオフィスにいる間に入社した社員に対して

“How are you?” と聞く

• 各社員が何回 “How are you?” と聞くかを求めよ

• 1 ≦ N ≦ 10^5

• 1 ≦ Si < Ti ≦ 2N

Page 12: Indeedなう B日程 解説

12

●部分点解法(30点)

• N ≦ 2000

• 素直に求める

• i,j の二重ループを回し、各 i について、

Si < Sj < Ti を満たすような j の個数を求める

Page 13: Indeedなう B日程 解説

13

●満点解法

• N ≦ 10^5

Page 14: Indeedなう B日程 解説

14

●満点解法

• Si の所が 1 でそれ以外が 0 であるような配列 A を考える

• すると、各 i について、A[Si] ~ A[Ti]の和を求めればいい

• 「ある区間の和」を高速に求めたい

• → 累積和

Page 15: Indeedなう B日程 解説

15

●満点解法

• B[i] = A[0] ~ A[i] の和

• という配列 B を計算しておくと、

• A[L] ~ A[R] の和 = B[R] - B[L-1]

• として区間の和を求めることができる

Page 16: Indeedなう B日程 解説

16

●満点解法

• 計算量は、

• 前処理:O(N)

• 各 i に対する計算:それぞれ O(1)

• となり間に合う

Page 17: Indeedなう B日程 解説

17

C: Palindrome Concatenation

Page 18: Indeedなう B日程 解説

18

●問題概要

• 回文をいくつか繋いで、文字列 S を作りたい

• 長さ i の回文を使うとコストが Ci かかる

• 文字列 S を作るための最小コストは?

• 1 ≦ N ≦ 5000

Page 19: Indeedなう B日程 解説

19

●部分点解法(40点)

• N ≦ 200

Page 20: Indeedなう B日程 解説

20

●部分点解法(40点)

• dp[i] = S の i 文字目までを作るときの最小コスト

• というDPをする

for (i = 0~N-1) { for (j = 1~N-i) { // 使う回文の長さ if (S[i]~S[i+j-1] が回文) { dp[i+j] = min(dp[i+j], dp[i]+C[j]) } } }

Page 21: Indeedなう B日程 解説

21

●満点解法

• N ≦ 5000

Page 22: Indeedなう B日程 解説

22

●満点解法

• ここを O(1) で計算できるようにしたい

for (i = 0~N-1) { for (j = 1~N-i) { // 使う回文の長さ if (S[i]~S[i+j-1] が回文) { dp[i+j] = min(dp[i+j], dp[i]+C[j]) } } }

Page 23: Indeedなう B日程 解説

23

●満点解法

• A[L][R] = S[L]~S[R] が回文かどうか

• というテーブルを前計算しておく

for (center = 0~N-1) { L = R = center while (L,R が範囲内かつ S[L] == S[R]) { A[L][R] = 回文である } L = center, R = center+1 while (L,R が範囲内かつ S[L] == S[R]) { A[L][R] = 回文である } }

Page 24: Indeedなう B日程 解説

24

●満点解法

• あるいは、メモ化再帰にする

bool isOK(L, R) { if (すでに計算した) return メモした結果 if (L > R) return true if (S[L] != S[R]) return false return isOK(L+1, R-1) }

Page 25: Indeedなう B日程 解説

25

●満点解法

• 計算量は、

• 前処理:O(N^2)

• DP:O(N^2)

• となり間に合う

Page 26: Indeedなう B日程 解説

26

D: Game on a Grid

Page 27: Indeedなう B日程 解説

27

●問題概要

• W×Hマスの二次元盤面がある • 各マスには非負整数の得点が割り当てられている • 上下左右に移動可能で,スタートとゴールが決まっている

(別にゴールに入っても移動し続けてよい) • あるマスを初めて訪問するとき,

1. (直前に居たマスの点数)×(そのマスの点数) 2. (そのマスの点数)

の両方を得点として得る • 達成できる最大得点を出力せよ.

• 1≦H,W≦100 • 0≦各マスの得点≦100

Page 28: Indeedなう B日程 解説

28

●考察1 全てのマスを辿る必要があるか?

• 全部のマスを辿ったほうが良さそう? →正しい 全得点が非負なので損することはない • 得点が0のマスを辿る必要はないことがあるが,

辿ったと考えて損はしない • とりあえず全マス辿るので,

Σ(全マスの点数)は確保 • 移動についてはまだ考えていない

1 1 0

1 2 3

2 2 3

ゴール地点

スタート地点

(いちおう適当な盤面の例)

Page 29: Indeedなう B日程 解説

29

●考察2 得点の発生する移動(1)

• 得点の発生した移動のみを視覚化してみると必ず木になる • どんな頂点から始めても任意のグリッドグラフの全域木が

作れるのでは?しかも辺の重みは無向? →どちらも正しい(木の形をどのように仮定しても作れる)

• スタートとゴールはどうでもよい

1 1 0

1 2 3

2 2 3

ゴール地点

スタート地点

(いちおう適当な盤面の例)

Page 30: Indeedなう B日程 解説

30

●考察2 得点の発生する移動(2)

• 移動で得られる最大得点はグリッドグラフの最大全域木

のコストに等しい(当然,辺のコストは2マスの点数の積) • 最大全域木を求めるには,最小全域木アルゴリズムでの

大小関係を真逆にして行えばよいことが知られている

• Prim法でもKruskal法でもよい

1 1 0

1 2 3

2 2 3

ゴール地点

スタート地点

(いちおう適当な盤面の例)

Page 31: Indeedなう B日程 解説

31

●解法

• マスを頂点,辺のコストを隣接する2マスの点数の積とした

無向グラフを考える

• 最大全域木のコストを何かしらのアルゴリズムで求める

• 最大全域木のコストにΣ(全マスの点数)を足したものを出力

• Kruskal法を用いる場合,辺のソートをバケットソートで 行えば時間計算量 O(WH×A(WH))

※A(n):=アッカーマン関数の逆関数 • 通常のKruskal法でもPrim法でも

O(WH log (WH))でも十分間に合う

Page 32: Indeedなう B日程 解説

32

E: Line up!

Page 33: Indeedなう B日程 解説

33

●問題概要

• 色々な高さの身長を持つ社員がN人が一列に並んでいる • 各人について,自分の左には自分の身長未満の人しかいない

状況にしたい • 隣り合う2人は入れ替わることができる

→そのとき身長の小さいほうの身長値だけのコストがかかる

• 並び替えにかかる最小の合計コストを出力 • もしそのような状況が存在しないなら-1を出力 • 1 ≦ N ≦ 10^5 • 1 ≦ 各人の身長H[i] ≦ 10^8

Page 34: Indeedなう B日程 解説

34

●自明な考察

• 同じ身長の組が存在すれば-1を出力

• もしそうでないとき,身長は全員相異なり 各人が昇順に並んだ状況のみが唯一の答え

• 以降のスライドでは全員の身長が相異なるものとして解説

Page 35: Indeedなう B日程 解説

35

●部分点解法(30点)

• 1≦N≦1000という部分点制約がある • 隣接する要素H[i],H[i+1]の入れ替えにmin(H[i],H[i+1])の コストがかかるバブルソート問題に帰着 • 無駄な入れ替えを行わない隣接要素を入れ替えるタイプの

整列プログラム(例えばバブルソート)を書けば間に合う

for(int i = 0 ; i < N-1 ; i++){ for(int j = 0 ; j+1 < N-i ; j++){ if( H[j] > H[j+1] ){ ans += H[j+1]; swap(H[j],H[j+1]); } } }

Page 36: Indeedなう B日程 解説

36

●考察

• 無駄なくバブルソートすることを考えると, ある要素の自分より大きい要素との交換回数は入れ替えの 仕方に関わらず一定

• 先ほどのソースコードを見ても分かるように 「ある要素の自分より大きい要素との交換回数」 ×「その要素の値」

の総和が求まればよい • 結局,実際に整列をシミュレーションしなくても,各要素に

ついて,「初期状態で自分より手前にある自分より大きい 要素の数」がその要素の交換回数

Page 37: Indeedなう B日程 解説

37

●解法

• その要素の交換回数(=自分より手前にある自分より大き い値の要素の数)を求めていく.

• 先頭からの逐次計算であれば,これは Segment Tree(*) と 呼ばれるデータ構造を用いると高速に計算できる.

(*)ここでは,「区間和の計算」「ある要素への加算クエリ」 を行える Segment Treeを指す.以降セグ木と呼ぶ.

Page 38: Indeedなう B日程 解説

38

●解法

• H[1],H[2],…,H[N]を座標圧縮する.そして各値が何番目に 来るかをX[1],X[2],…,X[N]としておく.

• セグ木のインデックスにその座標圧縮値を対応させ, [a,b]に対する計算クエリがH[a]以上H[b]以下の要素の数に なるように実装する.

• i=1..Nの順番に以下の処理を行う 1. H[i]より大きい値の数(セグ木の[X[i]+1,N]の区間和)を計算 2. (その計算値)×H[i]を全体的な答えに加算 3. セグ木のX[i]番目を+1する

• 全体的な答えを出力 • 算術オーバーフローに気をつけること

Page 39: Indeedなう B日程 解説

39

●解法

• セグ木の実装はBinary Indexed Treeが簡単

■計算量 • 座標圧縮はただのソートとユニーク処理,二分探索なので

合計でO(N log N) • セグ木構築の空間計算量はO(N)

(※明らかに 1≦X[i]≦N (1-indexedの場合)なので) • セグ木へのクエリにかかる時間計算量は合計でO(N log N)

• やっぱり全体として見ても

• 空間計算量 O(N) • 時間計算量 O(N log N)

Page 40: Indeedなう B日程 解説

40

●最後に

今回は,座標圧縮の方法やセグメントツリーの実装方法に ついては長くなるので省きました. 必要となる知識である • 座標圧縮 • セグメントツリー • SegmentTreeを用いたバブルソートの交換回数の計算 この3つの全ての実装方法がプログラミングコンテスト チャレンジブックに載っているので参考にするとよいでしょう