http://codeforces.com/problemset/problem/193/E
题意,给出一个n(0 <= n < 10^13), 问第一个模10^13后为n的Fibonacci数是多少,Fibonacci数就是0,1,1,2,3,5就是说第一个是0,第二个是1,然后后面每一个数是前两个的和。
思路:枚举发现Fibonacci最后一位数的循环节长为60,自然而然会觉得后两位循环节长肯定是60的倍数,后三位循环节长肯定是后两位的倍数,那么就可以一次求上去。枚举到7位后循环节长为1.5*10^7,13位的循环节长是1.5*10^13次方。
为什么要提到7位,我的做法就是枚举前1.5*10^7个Fibonacci数,如果后7位跟n的后7为相等,那么继续枚举的时候就可以每1.5*10^7一步。因为后7位跟n的后7相等的数的个数有限,所以时间复杂度应该不会太大。
#include
#include
#include
#include
#include
#include
using namespace std;
#define sqr(x) ((x) * (x))
#define two(x) (1 << (x))
#define order(x, y, z) ((x) <= (y) && (y) <= (z))
#define X first
#define Y second
typedef long long LL;
typedef pair pli;
const int N = 1;
const double eps = 1e-7;
const int ten = 10000000;
const int oo = 1000000000;
const int base = 1000000000;
const LL f = 10000000000000LL;
struct Integer {
LL a, b, c;
Integer(LL x = 0): a(0), b(x / base), c(x % base) {}
};
LL fib[15][3];
LL cycle[15];
Integer operator * (Integer a, Integer b) {
Integer product;
product.c = a.c * b.c;
product.b = a.b * b.c + a.c * b.b + product.c / base;
product.a = a.a * b.c + a.b * b.b + a.c * b.a + product.b / base;
product.c %= base;
product.b %= base;
product.a %= base;
return product;
}
Integer operator + (Integer a, Integer b) {
Integer sum;
sum.c = a.c + b.c;
sum.b = a.b + b.b + sum.c / base;
sum.a = a.a + b.a + sum.b / base;
sum.c %= base;
sum.b %= base;
sum.a %= base;
return sum;
}
Integer operator % (Integer a, LL b) {
Integer reminder;
reminder.a = 0;
if (b / base > 1) {
reminder.b = a.b % (b / base);
} else {
reminder.b = 0;
}
if (b > base) {
reminder.c = a.c;
} else {
reminder.c = b;
}
return reminder;
}
bool operator != (Integer a, LL b) {
return a.a != 0 || a.b != b / base || a.c != b % base;
}
LL mult(LL a, LL b, LL md) {
LL ret = 0;
for (; b; b >>= 1) {
if (b & 1) {
ret = (ret + a) % md;
}
a = (a + a) % md;
}
return ret;
}
LL getfib(LL n, LL md) {
n = n - 1;
LL base[2][2] = {{1, 1}, {1, 0}};
LL mat[2][2] = {{1, 0}, {0, 1}};
for (; n; n >>= 1) {
if (n & 1) {
LL temp[2][2] = {{mat[0][0], mat[0][1]}, {mat[1][0], mat[1][1]}};
mat[0][0] = (mult(temp[0][0], base[0][0], md) + mult(temp[0][1], base[1][0], md)) % md;
mat[0][1] = (mult(temp[0][0], base[0][1], md) + mult(temp[0][1], base[1][1], md)) % md;
mat[1][0] = (mult(temp[1][0], base[0][0], md) + mult(temp[1][1], base[1][0], md)) % md;
mat[1][1] = (mult(temp[1][0], base[0][1], md) + mult(temp[1][1], base[1][1], md)) % md;
}
LL temp[2][2] = {{base[0][0], base[0][1]}, {base[1][0], base[1][1]}};
base[0][0] = (mult(temp[0][0], temp[0][0], md) + mult(temp[0][1], temp[1][0], md)) % md;
base[0][1] = (mult(temp[0][0], temp[0][1], md) + mult(temp[0][1], temp[1][1], md)) % md;
base[1][0] = (mult(temp[1][0], temp[0][0], md) + mult(temp[1][1], temp[1][0], md)) % md;
base[1][1] = (mult(temp[1][0], temp[0][1], md) + mult(temp[1][1], temp[1][1], md)) % md;
}
return mat[0][0];
}
void init() {
cycle[0] = 1;
cycle[1] = 60;
LL pt = 100;
for (int i = 2; i <= 13; ++i) {
cycle[i] = cycle[i - 1];
LL x = getfib(cycle[i - 1] + 1, pt), y = getfib(cycle[i - 1], pt), z = getfib(cycle[i - 1] - 1, pt);
LL f0 = y, f1 = x;
while (f0 != 0 || f1 != 1) {
LL nf1 = (mult(f1, x, pt) + mult(f0, y, pt)) % pt;
LL nf0 = (mult(f1, y, pt) + mult(f0, z, pt)) % pt;
f1 = nf1;
f0 = nf0;
cycle[i] += cycle[i - 1];
}
pt *= 10;
}
for (int i = 1; i <= 13; ++i) {
fib[i][0] = getfib(cycle[i] - 1, f);
fib[i][1] = getfib(cycle[i], f);
fib[i][2] = getfib(cycle[i] + 1, f);
}
}
void print(Integer a) {
if (a.a == 0) {
if (a.b == 0) {
cout << a.c << endl;
} else {
cout << a.b;
stringstream s;
s << a.c;
string ss = s.str();
cout << string(9 - ss.size(), '0') << ss << endl;
}
} else {
cout << a.a;
stringstream sb, sc;
sb << a.b;
sc << a.c;
string ssb = sb.str();
string ssc = sc.str();
cout << string(9 - ssb.size(), '0') << ssb << string(9 - ssc.size(), '0') << ssc << endl;
}
}
int main() {
init();
LL n;
cin >> n;
if (n == 0) {
cout << 0 << endl;
} else if (n == 1) {
cout << 1 << endl;
} else {
LL t = 3, a0 = 1, a1 = 2, a2 = 1;
LL ans = (LL)oo * oo;
while (t <= cycle[7] && t <= ans) {
a2 = (a1 + a0) % ten;
a0 = a1;
a1 = a2;
++t;
if (a2 % ten == n % ten) {
int d = 1;
LL ac = t;
Integer x(fib[7][2]), y(fib[7][1]), z(fib[7][0]);
Integer f0(getfib(t - 1, f)), f1(getfib(t, f));
while (f1 != n && d <= 1000000 && ac < ans) {
Integer nf1((f1 * x + f0 * y) % f);
Integer nf0((f1 * y + f0 * z) % f);
f1 = nf1;
f0 = nf0;
ac += cycle[7];
d++;
}
if (!(f1 != n)) {
ans = min(ans, ac);
}
}
}
if (ans >= (LL)oo * oo) {
cout << -1 << endl;
} else {
cout << ans << endl;
}
}
return 0;
}
后来在某人的提点下发现从最低位逐位求上去就可以了。
新的代码,用python写的,时间比G++的还快了一倍。上面那个方法太搓了……
#! /usr/bin/env python
# @author: grastyele
import copy
def fib(x, Mod):
x = x - 1
base = [[1, 1], [1, 0]]
ret = [[1, 0], [0, 1]]
while x > 0:
if x & 1:
temp = copy.deepcopy(ret)
for i in range(0, 2):
for j in range(0, 2):
ret[i][j] = 0
for k in range(0, 2):
ret[i][j] = (ret[i][j] + base[i][k] * temp[k][j]) % Mod
temp = copy.deepcopy(base)
for i in range(0, 2):
for j in range(0, 2):
base[i][j] = 0
for k in range(0, 2):
base[i][j] = (base[i][j] + temp[i][k] * temp[k][j]) % Mod
x = x / 2
return ret[0][0]
def count_cycle():
size = [1, 60]
Mod = 10
for i in range(2, 14):
Mod = Mod * 10
x = fib(size[i - 1] - 1, Mod)
f0 = y = fib(size[i - 1], Mod)
f1 = z = (x + y) % Mod
size.append(size[i - 1])
while f0 != 0 or f1 != 1:
f1, f0 = (z * f1 + y * f0) % Mod, (y * f1 + x * f0) % Mod
size[i] += size[i - 1]
return size
def solve(f, size):
Mod = 10
f0 = 0
f1 = 1
ans = []
if f % 10 == 0:
ans.append(0)
elif f % 10 == 1:
ans.append(1)
for i in range(2, size[1]):
f1, f0 = (f0 + f1) % 10, f1
if f1 == f % 10:
ans.append(i)
for i in range(2, 14):
Mod = Mod * 10
for x in ans:
if x + size[i - 1] < size[i]:
ans.append(x + size[i - 1])
temp = []
for x in ans:
if fib(x, Mod) == f % Mod:
temp.append(x)
ans = copy.deepcopy(temp)
return min(ans) if len(ans) > 0 else -1
if __name__ == "__main__":
size = count_cycle()
print solve(int(raw_input()), size)