小 C 的数学题
题目描述
已知某函数
f
(
n
)
f(n)
f(n)的定义如下:
f
(
n
)
=
{
1
,
n
≤
2
f
(
n
−
1
)
+
f
(
n
−
2
)
+
⌊
l
o
g
2
n
⌋
,
n
≥
3
{\displaystyle f(n)={\begin{cases}1,&{n\le2}\\ f(n-1)+f(n-2)+\lfloor log_2n\rfloor,&{n\ge 3}\end{cases}}}
f(n)={1,f(n−1)+f(n−2)+⌊log2n⌋,n≤2n≥3
求函数
f
(
n
)
f(n)
f(n)的值,为了防止数据过大,你只需要输出答案对
1
0
9
+
7
10^9+7
109+7 取模之后的结果。
输入
最多十行,每行一整数 n n n, 1 ≤ n < 1 0 9 1\le n < 10^9 1≤n<109
输出
f ( n ) f(n) f(n)
样例输入
666
666666
666666666
样例输出
581824003
310868947
609519880
分析
乍一看是矩阵快速幂,但是随着
n
n
n的增大,
⌊
l
o
g
2
n
⌋
\lfloor log_2n\rfloor
⌊log2n⌋也会随之改变。
{%raw%}
(
f
n
f
n
−
1
1
)
=
(
1
1
⌊
l
o
g
2
n
⌋
1
0
0
0
0
1
)
(
f
n
−
1
f
n
−
2
1
)
{{\displaystyle {\begin{pmatrix}f_n\\f_{n-1}\\1\end{pmatrix}}}=\displaystyle {\begin{pmatrix}1&1&\lfloor log_2n\rfloor \\1&0&0\\0&0&1\end{pmatrix}}}{\displaystyle {\begin{pmatrix}f_{n-1}\\f_{n-2}\\1\end{pmatrix}}}
⎝⎛fnfn−11⎠⎞=⎝⎛110100⌊log2n⌋01⎠⎞⎝⎛fn−1fn−21⎠⎞{%endraw%}
变化中蕴含着不变性,在区间
n
∈
[
2
p
,
2
p
+
1
)
n\in [2^{p},2^{p+1})
n∈[2p,2p+1)内,
⌊
l
o
g
2
n
⌋
\lfloor log_2n\rfloor
⌊log2n⌋的值不变。因此我们只需要将
n
n
n分成若干个这样的区间,进行分段的矩阵快速幂即可。
代码
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=4;
const ll mod=1e9+7;
ll n;
struct matrix//定义矩阵
{
ll m[N][N];
matrix()//构造函数
{
memset(m,0,sizeof(m));
}
};
//定义矩阵乘法
matrix operator *(const matrix &x,const matrix &y)
{
matrix res;
int i,j,k;
for(i=1;i<=3;i++)
{
for(j=1;j<=3;j++)
{
for(k=1;k<=3;k++)
{
res.m[i][j]+=x.m[i][k]*y.m[k][j];
res.m[i][j]%=mod;
}
}
}
return res;
}
//矩阵快速幂
matrix quick_matrix_pow_mod(matrix a,ll b)
{
matrix res;
int i;
for(i=1;i<=3;i++) res.m[i][i]=1;//单位矩阵
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
matrix t,f;
void solve()
{
//t为系数矩阵
t.m[1][1]=1;
t.m[1][2]=1;
t.m[2][1]=1;
t.m[3][3]=1;
f.m[1][1]=f.m[2][1]=f.m[3][1]=1;
ll i;
for(i=3;i<=n;)
{
ll l,r,now=(ll)(log((double)i)/log((double)2));
l=i;
r=min(n,(1LL<<(now+1))-1);//分段
t.m[1][3]=now;//更改系数矩阵
matrix temp=quick_matrix_pow_mod(t,r-l+1);
f=temp*f;//得到新的f
i=r+1;//进入下一段
}
printf("%lld\n",f.m[1][1]);//输出
}
int main()
{
while(~scanf("%lld",&n)) solve();
return 0;
}