1 题意
已知
f
(
1
)
=
7
,
f
(
2
)
=
11
,
f
(
n
)
=
f
(
n
−
1
)
+
f
(
n
−
2
)
f(1)=7,f(2)=11, f(n) = f(n - 1) + f(n - 2)
f(1)=7,f(2)=11,f(n)=f(n−1)+f(n−2),问
f
(
n
)
f(n)
f(n)是否能被
3
3
3整除,其中
1
⩽
n
⩽
1
0
6
1 \leqslant n \leqslant 10^{6}
1⩽n⩽106。
链接:link
2 思路
原题可转化为:已知
f
(
1
)
=
1
,
f
(
2
)
=
2
,
f
(
n
)
=
(
f
(
n
−
1
)
+
f
(
n
−
2
)
)
m
o
d
3
f(1)=1,f(2)=2, f(n) = (f(n - 1) + f(n - 2)) \ mod \ 3
f(1)=1,f(2)=2,f(n)=(f(n−1)+f(n−2)) mod 3,问
f
(
n
)
f(n)
f(n)是否为
0
0
0,其中
1
⩽
n
⩽
1
0
6
1 \leqslant n \leqslant 10^{6}
1⩽n⩽106。
这样,这道题就转化成了HDU-1005,可以用循环节或矩阵快速幂来解题。
2.1 循环节
参考:link。
2.1.1 时间复杂度分析
- 计算前 11 11 11项的时间复杂度为 O ( 11 ) \mathcal{O}(11) O(11)。
- 在 0 ⩽ n ⩽ 8 0 \leqslant n \leqslant 8 0⩽n⩽8寻找 f ( n ) = f ( 9 ) , f ( n + 1 ) = f ( 10 ) f(n) = f(9),f(n+1) = f(10) f(n)=f(9),f(n+1)=f(10)的时间复杂度为 O ( 9 ) \mathcal{O}(9) O(9)。
- 计算 f ( n ) f(n) f(n)的时间复杂度为 O ( 1 ) \mathcal{O}(1) O(1)。
总的时间复杂度为 O ( 11 ) \mathcal{O}(11) O(11)。
2.1.1 代码
#include<iostream>
#include<cstdio>
using namespace std;
int a[11]={7,11},n;
int main(){
int step,first;
for(int i=2;i<=10;i++) a[i]=(a[i-1]+a[i-2])%3;
for(int i=0;i<=8;i++){
if(a[9]==a[i]&&a[10]==a[i+1]){
first=i;
break;
}
}
step=9-first;
while(~scanf("%d",&n)){
int res=n<9?a[n]:a[(n-first)%step+first];
if(res==0) puts("yes");
else puts("no");
}
return 0;
}
2.2 矩阵快速幂
关于矩阵快速幂的详细介绍请参考:link
这道题的矩阵快速幂的公式为:
[
f
(
n
)
f
(
n
−
1
)
]
=
[
1
1
1
0
]
n
−
1
[
f
(
1
)
f
(
0
)
]
\left[\begin{array}{c} f(n) \\ f(n-1) \end{array}\right]=\left[\begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array}\right]^{n-1}\left[\begin{array}{c} f(1) \\ f(0) \end{array}\right]
[f(n)f(n−1)]=[1110]n−1[f(1)f(0)]
2.2.1 时间复杂度分析
矩阵快速幂的时间复杂度为
O
(
m
3
log
(
n
)
)
\mathcal{O}(m^{3}\log(n))
O(m3log(n)),其中
m
m
m为矩阵大小,
n
n
n为幂指数。
对于这道题来说,时间复杂度为
O
(
8
log
(
n
)
)
\mathcal{O}(8\log(n))
O(8log(n))。
2.2.2 代码
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=15;
const int MOD=3;
int n,sz=2;
struct Matrix{
int dt[MAXN][MAXN];
Matrix(int tp=0){
for(int i=1;i<=sz;i++){
for(int j=1;j<=sz;j++){
if(i==j) dt[i][j]=tp;
else dt[i][j]=0;
}
}
}
Matrix operator+(const Matrix& a){
Matrix res;
for(int i=1;i<=sz;i++){
for(int j=1;j<=sz;j++){
res.dt[i][j]=(dt[i][j]+a.dt[i][j])%MOD;
}
}
return res;
}
Matrix operator-(const Matrix& a){
Matrix res;
for(int i=1;i<=sz;i++){
for(int j=1;j<=sz;j++){
res.dt[i][j]=(dt[i][j]-a.dt[i][j])%MOD;
}
}
return res;
}
Matrix operator*(const Matrix& a){
Matrix res;
for(int i=1;i<=sz;i++){
for(int j=1;j<=sz;j++){
for(int k=1;k<=sz;k++){
res.dt[i][j]=(res.dt[i][j]+dt[i][k]*a.dt[k][j])%MOD;
}
}
}
return res;
}
};
Matrix qpow(const Matrix& a,int b){
Matrix res(1),rem=a;
while(b){
if(b&1) res=res*rem;
rem=rem*rem;
b>>=1;
}
return res;
}
int main(){
while(~scanf("%d",&n)){
if(n==0){
puts("no");
continue;
}
Matrix mat;
mat.dt[1][1]=1;mat.dt[1][2]=1;
mat.dt[2][1]=1;
Matrix res=qpow(mat,n-1);
if((res.dt[1][1]*2+res.dt[1][2]*1)%3==0) puts("yes");
else puts("no");
}
return 0;
}
2.3 算法比较
从最坏的时间复杂度来看,循环节更具有优势,因为循环节最多计算 11 + 9 + 1 = 21 11+9+1=21 11+9+1=21次,而矩阵快速幂要计算 2 3 × log ( 1 0 6 ) = 159 2^{3} \times \log(10^{6}) = 159 23×log(106)=159次。