原题出自Ural 1133。
题目大意:扩展斐波那契数列在整数集上的定义,即不是从 F0=0 , F1=1 这样子开始的,下标可以是任意整数。给定这样一个序列中的两个下标,i,j,以及他们在序列中对应的值 Fi , Fj ,然后要求 Fn (n是输入的一个数字)。
思路1:由于题目里说到数据范围在
思路2:观察系数规律,i,j,n中的最小者是i,那么:
Fi+2=Fi+1+Fi ,
Fi+3=Fi+2+Fi+1=2Fi+1+Fi ,
Fi+4=Fi+3+Fi+2=3Fi+1+2Fi ,
…
Fi+n=fnFi+1+fn−1Fi .
其中 fn 是常规的斐波那契数列:{0, 1, 1, 2, 3, 5, 8, 13, 21, …, },这个规律我称之为“系数规律”。
现在只看i和j,假设j比i大,那么:
Fi,Fi+1,Fi+2,...,Fj ,有了“系数规律”,只需要求解 Fi+1 就好了,一旦知道了 Fi+1 ,那么就可以直接算出 Fn 了。怎么算出n呢?假设n比i大,那么根据上面推导的“系数规律”,有:
贴代码如下:
#include <iostream>
using namespace std;
typedef long double LD;
const int MAX = 2005;
LD f[MAX];
int main() {
// 预处理f数组
f[0] = 0, f[1] = 1;
for (int i = 2; i < MAX; ++i)
f[i] = f[i-1] + f[i-2];
int i, j, n;
LD Fi, Fj, Fn, Fii;
cin >> i >> Fi >> j >> Fj >> n;
if (i > j) {
swap(i, j);
swap(Fi, Fj);
}
// 因为数组的下标应该是非负的
i += 1000, j += 1000, n += 1000;
// 求出F(i+1)
Fii = (j - i == 1) ? Fj : ((Fj - f[j-i-1]*Fi) / f[j-i]);
if (n == i)
Fn = Fi;
else if (n - i == 1)
Fn = Fii;
else if (n - i > 1) {
// 注意不能直接用公式:Fn = f[n-i]*Fii + f[n-i-1]*Fi,中间的乘法可能会爆
for (int index = i+2; index <= n; ++index) {
Fn = Fii + Fi;
Fi = Fii;
Fii = Fn;
}
}
else {
for (int index=i-1; index >= n; --index) {
Fn = Fii - Fi;
Fii = Fi;
Fi = Fn;
}
}
cout << (int)Fn << endl;
return 0;
}
好吧,虽然有点丑,但我觉得还是把算法说到位了。
最后再说一句,这道题这么敏感的计算,再次演示了“编译器有毒”系列作品,用Visual C++ 2013的人目测都会死得很惨,因为我就是用它,然后一直WA 10了,改用G++ 4.9就好了╮(╯▽╰)╭。。。。。
最后再贴我用python 3的代码(矩阵快速幂+二分):
# URAL 1133
Unit = [[1, 1], [1, 0]]
def mul(a, b):
ans = [[0 for i in range(0, len(a))] for j in range(0, len(b))]
for i in range(0, len(a)):
for j in range(0, len(b)):
for k in range(0, len(b)):
ans[i][j] += a[i][k] * b[k][j]
return ans
def POW(n):
'''注意不能写n /= 2,会被python当做浮点数,应该是用位运算'''
a = Unit
b = [[1, 0], [0, 1]]
while n > 0:
if n & 1:
b = mul(a, b)
a = mul(a, a)
n >>= 1
return b
def cal(i, Fi, j, Fj, n):
mat = POW(j-1)
[Left, Right] = [int(-2*1e9), int(2*1e9)]
while Left <= Right:
mid = (Left + Right) >> 1
x = mat[0][0] * mid + mat[0][1] * Fi
if x > Fj:
Right = mid - 1
elif x < Fj:
Left = mid + 1
else:
break
if n > 0:
mat = POW(n - 1)
print(mid*mat[0][0] + Fi*mat[0][1])
else:
[Fa, Fb] = [mid, Fi]
for i in range(n, 0):
[Fa, Fb] = [Fb, Fa - Fb]
print(Fb)
if __name__ == '__main__':
[i, Fi, j, Fj, n] = [int(i) for i in input().split(' ')]
if i > j:
[i, j] = [j, i]
[Fi, Fj] = [Fj, Fi]
j -= i
n -= i
i = 0
cal(i, Fi, j, Fj, n)