51nod 1365 Fib(N) mod Fib(K)
Description
Fib(N)表示斐波那契数列的第N项(F(0) = 0, F(1) = 1),给出N和K,求Fib(N) mod Fib(K)。由于结果太大,输出Mod 1000000007的结果。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 50000)
第2 - T + 1行:每行2个数N, K(1 <= N, K <= 10^18)
Output
共T行:对应Fib(N) mod Fib(K)的结果再Mod 1000000007。
Input示例
2
5 5
13 5
Output示例
0
3
解题思路
参考博客http://blog.youkuaiyun.com/acdreamers/article/details/21822165
主要是公式的推导,按照自己的理解整理一下
由于公式 F(m+n)=F(m+1)F(n)+F(m)F(n-1),
所以F(n)%F(k)=F(n-k+k)%F(k)=[F(n-k+1)F(k)+F(n-k)F(k-1)]%F(k)=F(n-k)F(k-1)%F(k)=……=F(n%k)
F(k−1)nk
F
(
k
−
1
)
n
k
%F(k).
由于
F(n)2=(−1)n+1+F(n−1)F(n+1)
F
(
n
)
2
=
(
−
1
)
n
+
1
+
F
(
n
−
1
)
F
(
n
+
1
)
,
所以有
F(k−1)2=(−1)k+F(k−1)F(k)
F
(
k
−
1
)
2
=
(
−
1
)
k
+
F
(
k
−
1
)
F
(
k
)
即
F(k−1)2
F
(
k
−
1
)
2
%
F(k)=(−1)k
F
(
k
)
=
(
−
1
)
k
.
令n%k=x,
nk=y
n
k
=
y
,
则原式=
[F(x)F(k−1)y]
[
F
(
x
)
F
(
k
−
1
)
y
]
%F(k).
当k 为偶数时:
F(k−1)2
F
(
k
−
1
)
2
%F(k)=1.
则当y 为偶数时,原式=F(x)%F(k),
当y 为奇数时,原式=[F(x)*F(k-1)]%F(k);
当k 为奇数时:
F(k−1)2
F
(
k
−
1
)
2
%F(k)=-1.
当y 为偶数时(代表y个F(k-1)可以两两凑成一对).
当
y2
y
2
为偶数时(代表有偶数个-1相乘),则原式=F(x)%F(k),
当
y2
y
2
为奇数时(代表最后会留下一个-1),则原式=[-F(x)]%F(k)=F(k)-F(x);
当y 为奇数时(代表会留下一个F(k-1)无法配对).
当
y2
y
2
为偶数时,原式=[F(x)F(k-1)]%F[k],
当
y2
y
2
为奇数时,原式=[-F(x)F(k-1)]%F(k)=F(k)-F(x)F(k-1)%F(k);
接下来简化F(x)*F(k-1):
利用性质,当n>=1,r>=2,有F(n)F(n+r-1)-F(n+1)F(n+r-2)=
(−1)n+1F(r−2)
(
−
1
)
n
+
1
F
(
r
−
2
)
,
令x=n+1,k-1=n+r-2,则有F(x-1)F(k)-F(x)F(k-1)=
(−1)xF(k−x)
(
−
1
)
x
F
(
k
−
x
)
,
可转化为 F(x)F(k-1)-F(x-1)F(k)=
(−1)x+1F(k−x)
(
−
1
)
x
+
1
F
(
k
−
x
)
,
所以 F(x)F(k-1)%F(k)=
(−1)x+1F(k−x)
(
−
1
)
x
+
1
F
(
k
−
x
)
.
则当x 为奇数时,原式=F(k-x),
当x 为偶数时,原式=[-F(k-x)]%F(k)=F(k)-F(k-x).
对于求斐波那契,使用矩阵快速幂来优化就可以了.
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
#define IO ios::sync_with_stdio(false);\
cin.tie(0);\
cout.tie(0);
struct matrix{
ll m[2][2];
}ans,base;
//矩阵相乘
matrix multi(matrix a,matrix b)
{
matrix tmp;
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
tmp.m[i][j]=0;
for(int k=0;k<2;k++)
tmp.m[i][j]=(tmp.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod;
}
}
return tmp;
}
//矩阵快速幂
ll getFib(ll n)
{
base.m[0][0]=base.m[0][1]=base.m[1][0]=1;
base.m[1][1]=0;
ans.m[0][0]=ans.m[1][1]=1;
ans.m[0][1]=ans.m[1][0]=0;
while(n)
{
if(n&1)
ans=multi(ans,base);
base=multi(base,base);
n/=2;
}
return ans.m[1][0]%mod;
}
//返回fib(n%k)*fib(k-1)%mod t=n%k,m=k-1
ll getmul(ll t,ll m)
{
if(t%2==1)
return getFib(m-t+1);
else
return ((getFib(m+1)-getFib(m-t+1))%mod+mod)%mod;
}
ll solve(ll n,ll k)
{
ll x=n%k,y=n/k;
if((k&1)==0)
{
if((y&1)==0)
return getFib(x);
else
return getmul(x,k-1);
}
else
{
if((y&1)==0&&((y/2)&1)==0)
return getFib(x);
else if((y&1)==0&&((y/2)&1))
return ((getFib(k)-getFib(x))%mod+mod)%mod;
else if((y&1)&&((y/2)&1)==0)
return getmul(x,k-1);
else if((y&1)&&((y/2)&1))
return ((getFib(k)-getmul(x,k-1))%mod+mod)%mod;
}
}
int main()
{
IO;
int T;
ll n,k;
cin>>T;
while(T--)
{
cin>>n>>k;
cout<<solve(n,k)<<endl;
}
return 0;
}
蓝桥杯 斐波那契
问题描述
斐波那契数列大家都非常熟悉。它的定义是:
f(x) = 1 …. (x=1,2)
f(x) = f(x-1) + f(x-2) …. (x>2)
对于给定的整数 n 和 m,我们希望求出:
f(1) + f(2) + … + f(n) 的值。但这个值可能非常大,所以我们把它对 f(m) 取模。
公式如下
但这个数字依然很大,所以需要再对 p 求模。
输入格式
输入为一行用空格分开的整数 n m p (0 < n, m, p < 10^18)
输出格式
输出为1个整数,表示答案
样例输入
2 3 5
15 11 29
样例输出
0
25
解题思路
和上一题不同的是,这里是求和再取模,这个很容易解决,就是利用规律
∑ni=1f(i)=f(n+2)
∑
i
=
1
n
f
(
i
)
=
f
(
n
+
2
)
.
另外还有一个问题就是这个取模的范围也是1e18,这就导致在矩阵相乘时可能出现1e18*1e18的情况,所以我们还是需要使用类似于矩阵快速幂的思想来解决这个问题,具体见代码.
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IO ios::sync_with_stdio(false);\
cin.tie(0);\
cout.tie(0);
struct matrix
{
ll m[2][2];
} ans,base;
ll mod;
ll mul(ll a ,ll b)
{
a=(a%mod+mod)%mod;
b=(b%mod+mod)%mod;
if(a<b) swap(a,b);
ll ans=0;
while(b)
{
if(b&1) ans=(ans+a)%mod;
a=(a*2)%mod;
b/=2;
}
return ans;
}
matrix multi(matrix a,matrix b)
{
matrix tmp;
for(int i=0; i<2; i++)
{
for(int j=0; j<2; j++)
{
tmp.m[i][j]=0;
for(int k=0; k<2; k++)
tmp.m[i][j]=(tmp.m[i][j]+mul(a.m[i][k],b.m[k][j]))%mod;
}
}
return tmp;
}
ll getFib(ll n)
{
base.m[0][0]=base.m[0][1]=base.m[1][0]=1;
base.m[1][1]=0;
ans.m[0][0]=ans.m[1][1]=1;
ans.m[0][1]=ans.m[1][0]=0;
while(n)
{
if(n&1)
ans=multi(ans,base);
base=multi(base,base);
n/=2;
}
return ans.m[0][1]%mod;
}
ll getmul(ll t,ll m)
{
if(t%2==1)
return getFib(m-t+1);
else
return ((getFib(m+1)-getFib(m-t+1))%mod+mod)%mod;
}
ll solve(ll n,ll k)
{
ll x=n%k,y=n/k;
if((k&1)==0)
{
if((y&1)==0)
return getFib(x);
else
return getmul(x,k-1);
}
else
{
if((y&1)==0&&((y/2)&1)==0)
return getFib(x);
else if((y&1)==0&&((y/2)&1))
return ((getFib(k)-getFib(x))%mod+mod)%mod;
else if((y&1)&&((y/2)&1)==0)
return getmul(x,k-1);
else if((y&1)&&((y/2)&1))
return ((getFib(k)-getmul(x,k-1))%mod+mod)%mod;
}
}
int main()
{
IO;
ll n,k;
cin>>n>>k>>mod;
ll ans = solve(n+2,k);
if(ans==0)
cout<<getFib(k)-1<<endl;
else
cout<<ans-1<<endl;
return 0;
}
PS:
1.为什么用类似mul()的方法处理F(x)*F(k-1)不行;
2.a%b%c
≠
≠
(a%c)%b,(a*b)%c%d
≠
≠
(a%d)*(a%d)%c,所以中间的取模处理是为什么;
3.性质:当n>=1,r>=2,有F(n)F(n+r-1)-F(n+1)F(n+r-2)=
(−1)n+1F(r−2)
(
−
1
)
n
+
1
F
(
r
−
2
)
.