O(sqrt(N))のアルゴリズム
これはCompetitive Programming Advent Calendar Div2012に投稿された記事です。
さて、みたいな数値を見ると、は無理だからかとか、もしくはだとと思ってしまう人は多いでしょう。ですが、まで行かなくてももう少し悪い計算量が想定解の場合もあります。というわけで、今回はだとかという、ルートが入ったあまり見ない計算量のアルゴリズムを紹介します。
等差数列の和
となるようなyを見つける問題はO(1)で解けますが、Nになるまで愚直に足していけばで解けます。こんなもんどこで使うんだと思う人もいるかもしれませんが、手計算が面倒な場合とか証明したい場合に使います。
floorの分割
みたいな式があったとしましょう。ただし、はで計算できるとします。
この式を愚直に計算すると、もちろんかかってしまいます。ここでが同一のものをまとめて処理することにします。するととなります。ただしSはfloor(N/x)の値域、c(y)はfloor(N/x)=yとなるxの個数とします。
Sのサイズを計算すると実はで抑えることができます。なぜ、Sのサイズがで抑えることができるかとういと、となるyはもちろん個以下しか存在しません。一方でとなるにはという条件を満たさなければならないのでこれも個以下しか存在しません。なのでこの式をで計算することができます。
問題例:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3558
離散対数問題
離散対数問題とはa,b,Nがgivenの下でとなるようなmを見つける問題です。
この問題は効率的な解法が見つかってませんが*2、baby-step giant-stepを使えばで解くことができます。
まず、と分割し、までは普通に計算しハッシュなどに結果を保持しておきます。次にを考えます。yを[0,]の範囲で全探索すると右辺は値が固定されるので、先程計算しておいたハッシュに値が含まれているかどうか確認すればxが求まります。
前処理も全探索の処理も両方なので、で答えが見つかります。
問題例:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=229&page=show_problem&problem=3067
平方根分割
主にクエリの平方根分割と配列の平方根分割があります。
クエリの平方根分割はクエリを個処理するごとにデータ構造を作り直し、それぞれの区間で使われていない値を無視することにより各クエリをくらいで処理できるようにします。
配列の平方根分割は配列をのブロックごとに分割し、[l,r]の範囲クエリが来た時にブロックを全て含む部分は高速に処理し、端っこの部分は愚直に処理します。ブロックの数は個しかないし、端っこの部分は個のデータしかないので1つのクエリあたりで処理できます。
ここらは毎回使うデータ構造が微妙に異なってくるので、回数をこなして慣れましょう。ちなみに平方根分割の問題はだいたい平衡二分木や動的木などを使えば計算量が落ちる上に、ライブラリを持っているとそっちの方が実装が楽になります。
問題例(クエリの平方根分割):http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2235
問題例(クエリの平方根分割):https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=288&page=show_problem&problem=1962
問題例(配列の平方根分割):http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=242&page=show_problem&problem=3154
問題例(配列の平方根分割):http://codeforces.com/problemset/problem/121/E
問題例(Block Linked List):http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=229&page=show_problem&problem=3073
二部マッチング
Hopcroft-Karpアルゴリズムを使えば二部マッチングはで解けます。
Hopcroft-KarpとはDinicのアルゴリズムを二部マッチングに適用したものです。つまり、フェーズごとにレベルグラフ*3を作り、各フェーズで増加パスが存在する限りフローを流すアルゴリズムです。
計算量の解析としては、まず容量が1なので各フェーズの処理がに落ちます。次に、のフェーズを過ぎると増加パスの長さが以上になりますが、解のマッチングとの対象差(辺集合のxor)を比べた時に残る点素な増加パスはたかだか個なので*4、残りのフェーズの数は個で抑えられます。両方を合わせるととなります。
問題例:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2279
最大流問題
最大流問題のアルゴリズムはEdmonds-KarpやDinic、藤重のアルゴリズムなど色々あります。
そして、Goldberg-Tarjanアルゴリズムは最大流問題をで解きます。
藤重のアルゴリズム()は計算量に容量がはいっていて、ナイーブなDinicはなので、これらのアルゴリズムよりも理論上は高速に動きます*5。計算量の証明をすると長くなるので省略しますが、各フェーズで不飽和プッシュの起こる回数を以下と以上に分割することによりがかかっています。
さて、Dinicの計算量はと書きましたが辺の容量が1であるという制約がかかっている場合はまで計算量が落ちます。なぜになっているかというと、回目のフェーズであればレベルグラフのd層目とd+1層目の間に辺が以下しかない所が出てくるため*6、残りの増加パスはたかだか個しかない、つまり残りフェーズがたかだか個しかないことが言えるからです。も辺が頂点に変わっただけでほぼ同じ証明でできます。
まとめ
が入っているアルゴリズムを色々紹介しました。
の方はたびたび出ますが、時々でいいからのことも思い出してやってください。
たまに出てくるので。
参考文献
Wikipedia Baby-step giant-step:http://en.wikipedia.org/wiki/Baby-step_giant-step
Wikipedia Hopcroft-Karp:http://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm
組合せ最適化:http://www.amazon.co.jp/dp/4431100210
どっかの大学の講義資料:http://people.orie.cornell.edu/dpw/orie633/LectureNotes/lecture9.pdf