LoginSignup
2
2

More than 5 years have passed since last update.

Codeforces #257 div.2 B

Last updated at Posted at 2014-07-26

概要

http://codeforces.com/contest/450/problem/B
昨日に引き続き。
なかなか楽しい問題でした。
今回はScalaっぽいこともできます。

問題

超シンプル。
以下の数式がある。
$f_n$の値を$10^9+7$で剰余を取り出力せよ。

f_1 = x; f_2 = y;\forall i (i \leq 2), f_i = f_{i-1} + f_{i+1}
1 \leq n \leq 10^9

アルゴリズム

式変形

まずは、馬鹿正直に与えられた数式を使ってみます。
早速サンプル1、$x=2,y=3,n=3$の時を考えましょう。
数式に当てはめて・・・

f_3 = f_2 + f_4 \\
 = 3 + f_4 \\
 = ...

$f_4$ が出ちゃったのォ!? ダレカタスケテー

というわけで、式変形しましょう。

(1) f_i = f_{i-1} + f_{i+1} \\
(2) f_{i+1} = f_i - f_{i-1} \\
(3) f_i = f_{i-1} - f_{i-2}

(3)を使うと、

f_1 = x \\
f_2 = y \\
f_3 = f_2 - f_1 = y - x \\
f_4 = f_3 - f_2 = (y-x) - y = -x

やったね!どこまでも行ける!

速度的に

どこまでも行けるようになったのはいいですが…。
nが$10^9$とデカイので、O(n)でも死にそう。
O(logn)とかO(1)にしないと競プロ的にはアウト。

O(n)でもダメということは、フィボナッチとかでよくあるメモ化してもダメ。
冪乗でフィボナッチ数を求めるような、画期的な何かが必要ですね。

式変形アゲイン

というわけで、再度式を眺めてみます。
こういう時は、しばらく項を求めてみると何かが見えるもんです。
実際にしばらく求めてみましょう。

f_5 = f_4 - f_3 = -x - (y-x) = y \\
f_6 = f_5 - f_4 = y - (-x) = y + x \\
f_7 = f_6 - f_5 = (y+x) - y = x \\
f_8 = f_7 - f_6 = x - (y+x) = y \\
f_9 = f_8 - f_7 = y - x

$f_7$が・・・。
$f_1$に帰っちゃったのォ!?ダレカタス(ry

不思議ですが、この性質を使えばnがいくら大きかろうか、$f_1$~$f_6$のどれかを使えばOK。
O(1)で求められます。

剰余

言語によって、以下のコードの結果が異なります。

-1 % 10

これが9になるか、-1になるかは言語それぞれですが、Scalaでは-1のままです。困った。

しょうがないので、以下のテクニックを使って、Mで剰余を取った時負数にならないようにします。

( ( -1 % M ) + M ) % M

解答

今回はJava的な書き方とScala的な書き方を分けてみます。

Java的記述

6要素を持つ配列を用意して、そこに入れます。
念のため、Int - Intをする時にも剰余を取っておきます。

import scala.math.BigInt

object Main extends App {
  val sc = new java.util.Scanner(System.in)
  val x, y, n = sc.nextInt

  def solve( x: Int, y: Int, n: Int ): Int = {
    val M: Int = 1e9.toInt + 7

    val newN = (n-1) % 6

    var memo = new Array[Int](6)
    memo(0) = x
    memo(1) = y
    for( i <- (2 until 6) ) {
      memo(i) = ( memo(i-1) - memo(i-2) ) % M
    }

    ( ( memo(newN) % M ) + M ) % M
  }

  println( solve( x, y, n ) )
}

分かりやすいですね。
これで通ります。

Scala的記述

関数型言語的には、無限リストを作ってn番目の要素を取り出すのが美しい気がします。
ついでにBigIntを使って、Int - Intをしても大丈夫なようにしておきます(BigIntの計算速度わからんけど)。

import scala.math.BigInt

object Main extends App {
  val sc = new java.util.Scanner(System.in)
  val x, y, n = sc.nextInt

  def solve2( x: Int, y: Int, n: Int ): Int = {
    val M: Int = 1e9.toInt + 7

    val newN = (n-1) % 6

    def tail(a: BigInt, b: BigInt): Stream[BigInt] = a #:: tail(b, b - a)
    val res = tail(x, y)(newN)
    ( ( ( res % M ) + M ) mod M ).toInt
  }

  println( solve( x, y, n ) )
}

なんとなく、エレガント。

結果

Time: 233ms
Memory: 0 KB

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2