SRM 656

出ました。

250

N個のパンケーキがあり、i番目のパンケーキの幅はi+1で美味しさはd[i]です。以下のような操作をしてタワーを作ります。

  • 残っているパンケーキをランダムに1つ選びます。(残っていなければ終了)
  • そのパンケーキの幅がタワーの一番上のパンケーキの幅より大きければ終了
  • そうでないなら積んで、最初に戻る

タワーに積まれたパンケーキの美味しさの和の期待値を求めよ。

解法

期待値の線形性を使う。

つまり「Σ d[i] * (i番目のパンケーキが使われる確率)」で答えを求める。

dp[i][j] = i個のパンケーキを既に使っていて、一番上のパンケーキの幅がjである確率

というDPをして、答えは「Σ dp[i][j]*d[j]」で求めるといい。

初期化は、番兵をおいて、dp[0][N]とすると楽。

ちなみに遷移は、

if (k < j) dp[i+1][k] += dp[i][j]*(N-i)

みたいな感じ?

500

長さNのパーミュテーションであって、pos[i]の箇所だけp[i]<p[i+1]でそれ以外はp[i]>p[i+1]であるようなものを数え上げよ。
※ len(pos) = Mとします。

  • N ≦ 10^6
  • M ≦ 2500
解法

包除原理

まず、「posのところの条件を満たす必要がない」という問題を考えます。

単調減少でなければならない区間がM+1個出てきますよね。i個目の区間の長さをa[i]とします。

「posのところの条件を満たす必要がない」という問題の答えは「N!/mul(a[i]!)」です。

では、「posのところの条件を少なくともk個満たさない」という問題を考えます。

これは、

dp[i][j] = i番目の区間までを見て今までで少なくともj個の条件を満たさないものの個数/N!(i=0~M,j=0~M)

みたいなDPを考えるとできます。

「/N!」とかいてありますが、これは計算を簡略化するための工夫で、最後に*N!をするという感じです。

遷移は

dp[i][j] = Σ dp[i-k][j+k]*(Σa[i-k]~a[i])!

みたいな感じです(適当に書いたので細かい±1とかがずれてるかもしれませんが)

で、包除原理を使って答えを求めます。

このままだと3乗ですが、包除原理においては満たす条件の個数はどうでもよくて、その偶奇だけに興味があるので、dp[M][2]のDPにすると2乗になります。

ちなみに、さらにまとめてdp[M](dp[i][0]-dp[i][1]を記録する)にしても良いです。

雑な解説なので、足りないところがあれば考えてみるか、聞いてください。

(本番では階乗の配列を2505で取ってて落ちました。つらい)

文字列の周期性判定

以前の記事「文字列の頭良い感じの線形アルゴリズムたち」で、

この配列を使うと文字列検索や周期性の判定などが高速に行えるようになるのですが、
その辺りの説明は他のサイトにお任せします。

と書いたところ、周期性の判定の方に関して「"他のサイト"なんてものはない」と言われてしまったので、書きます。

文字列 S が与えられる。S[0~i] の最小の周期長を全部まとめて O(N) で求めよ。

という問題を考えます。例えば、

abababcaa
122222778

という感じ。(上がSで下が答えの配列)

例えば、"ababa"なら"ab"を繰り返せばいいので 2 という感じ。

解法

KMPでテーブルを作ります。

んで、「i - KMP[i]」が各場所の答え(1-indexed)

簡単な解説

「最小の周期長」=「k 文字ずらしたものが元の文字列と一致するような最小の k (k>0)」

例えば、"ababa"は

ababa
__ababa

2 文字ずらした時に初めて同じ列にある文字同士が一致するので 2 が答え。

この同値関係は この記事 が参考になるかも

で、「k 文字ずらしたものが元の文字列と一致するようなhogehoge」ってKMPそのものみたいなものですよねという。

練習問題

SAT solverでパズルを解いた話

SAT solverで「数独」「美術館」「ひとりにしてくれ」を解きました。

Githubにあげました。

めっちゃ楽しかったので準急に多謝だー。(JOI春合宿で講義をしてくれた)

準備

SAT solverをインストールしましょう。

僕はminisatにしました。(なんか入れるのに苦労したけど)

SATを知らない人は適当に調べましょう。

流れとしては、

  • パズルを連言標準形(CNF)の論理式にして
  • SAT solverに投げて
  • ビジュアライズする

って感じです。

数独

多分一番簡単です。

「x1,x2,...,x9 のうち、ちょうど1つだけがtrue」

という式が書ければ勝ちです。これは、

  • x1 or x2 or ... or x9 (1つ以上であるという制約)
  • 全てのi,jのペアについて、!xi or !xj (2つ以上ないという制約)

で実現できます。

あとは、X_i,j,k(i行目j列目がkであるならtrue)という変数を使って

  • 行にちょうど1つだけkがある
  • 列にちょうど1つだけkがある
  • 3x3のグループにちょうど1つだけkがある
  • 各マスにはちょうど1つだけ数が割り当てられている

という条件を書いてやればいいです。

美術館

これも基本的

変数は、X_i,j = i行目j列目に明かりを置くならtrue です。

  • 照らしあってはいけない
  • 照らされていないマスがあってはいけない
  • 黒マスに明かりがあってはいけない
  • 周りに 0 個ある
  • 周りに 1 個ある
  • 周りに 3 個ある
  • 周りに 4 個ある

は簡単(実際にプログラムとして書くのはちょっと面倒だけど)で、問題は

  • 周りに 2 個ある

です。これは、

  • どの3個を取り出してorを取ってもtrue
  • どの3個を取り出して否定のorを取ってもtrue

の2つの条件にすると楽に書けます。

ちなみに、フィールドの周囲に番兵を置くとかなり実装が楽になります。

ひとりにしてくれ

  • 各列に同じ数が2つ以上残ってはならない
  • 黒マスが隣り合ってはならない

は簡単。

  • 白マスは連結でなければならない

ひぇーっ!?こんなのできるんでしょうか?

かなりのパズルが連結性判定を必要とするので、これができないとなかなか厳しいです。

連結性判定は、いろいろな方法がありそうですが、僕が今回採用したのはBFSっぽい方法です。

イメージとしては、

  • 適当に根を決めてBFSをするときの、各ステップでのvisitedの配列を変数にする。

という感じ。式を書く方針はこんな感じ。

  • Y_i,j : i行目、j列目を黒く塗るならtrue
  • X_i,j,k : i行目、j列目、BFSのkステップ目
  • X_i,j,0 たちのうち、1つだけがtrue
  • BFSの総ステップ数を T とすると、白マスの X_i,j,T は全部true
  • 黒マスの X_i,j,k は全部false
  • X_i,j,k+1 がtrueなら、X_ai,aj,kのうちいずれかはtrue((ai,aj)は(i,j)に隣接するマス)

※「A ならば B」は 「!A or B」と変換できます

変数も節もO(マスの個数^2)個でいい感じです。

ちなみに、17x17のを1つやらせてみたら20秒で答えが出ました。感動!

自作問題リスト

自作問題をgoogle spreadsheetにまとめました。

snuke問題 難易度表

evimaさんが作っておられたのを見て、確かスプレッドシートに自作問題をまとめておくのはいいなぁと思い、作りました。

evima's problems

りんごさんのもあります。

今年の目標を振り返る

人生ゲーム 2014 - あなたは嘘つきですかと聞かれたら「YES」と答えるブログ
採点します。
algorithmのレート:6/6
思ったより上がった。
marathonの参加回数:1/6
機械学習系にも出ないと回数が増えないなー。
marathonのレート:3/6
3回しか出てないにしては頑張ってる。
CodeForcesのレート:5/5
高すぎるかと思ったら達成できた。
ICPC成績:0/2
来年こそは!
ICPC練習:1/2
オンサイト進出回数:5/5
日本のオンサイト数えていいんだっけ。まあいいや。
個人の最高順位:5/5
UTPC3位で既に達成していたんですが、優勝までできるとは。
writer定期:3/5
writerその他:1/3
意識:0/5
作品:5/10
あんまり作らなかったなー。
スキル:5/10
採点しにくい。
大学:0/10
大学生の鑑(あんまり覚えてない)
雨でもないのに外出しなかった日数:0/4
外出しなかった日には雨が降るべき。
添い寝:0/0
max(0,score)
その他:6/16
適当。

コンテスト系:30/45
その他プログラミング:10/25
大学:0/10
その他:6/20

合計:46/100
評価:D

欠点、留年不可避。
もう一回2014年やります。