f(1)=f(2)=1 f ( 1 ) = f ( 2 ) = 1
f(n)=(A∗f(n−1)+B∗f(n−2))mod7 f ( n ) = ( A ∗ f ( n − 1 ) + B ∗ f ( n − 2 ) ) mod 7
1≤A,B≤1,000,1≤n≤108 1 ≤ A , B ≤ 1 , 000 , 1 ≤ n ≤ 10 8
求 f(n) f ( n )
看到此题,一种比较平凡的思路就是找规律。很多人的直觉是,循环节的长度必然是49。想到这种想法,主要的理由就是对于一对(A, B)的取值, (f(n),f(n−1)) ( f ( n ) , f ( n − 1 ) ) 的值对最多有49种,所以必然能在50步内找到循环节。由此形成如下代码:
#include
using namespace std;
const int N = 50;
int s[N];
int main(){
int a, b, n;
while(scanf("%d%d%d", &a, &b, &n), a+b+n){
s[0] = s[1] = 1;
int T = 49;
for(int i = 2; i < N; i++){
s[i] = (a*s[i-1]+b*s[i-2]) % 7;
if(s[i] == 1 && s[i-1] == 1){
T = i-1;
break;
}
}
printf("%d\n", s[(n+T-1)%T]);
}
return 0;
}
提交后,我们发现这份代码是AC的。但是有一组样例,这样的写法是明显错误的:
我们可以重新思考题意并建立模型,如果我们把任一对相邻的 f(x) f ( x ) 值看作一个点即 V={(f(i−1),f(i))} V = { ( f ( i − 1 ) , f ( i ) ) } 那么我们可以知道任一 vi v i 有且仅有一条可能指向自身的边,即 f(a,b):vi→vj f ( a , b ) : v i → v j 。显然,这是一个49个点组成的图,且每个点有且仅有一条出边。
按直觉我们可以认定这个图必然有环,如果需要严谨证明的话,大概只能用到归纳法了:
若存在有n个点,每个点有一条出边的无环图,那么它必然是由n-1个点的具有同样性质的无环图产生的。(因为一个有环图不能通过增加一个点与一条点上的出边变得无环)
而已知当n=2时,不存在一个2个点,每个点均有一条出边的无环图。
所以有n个点,每个点有一条出边的图必有环。
那么重点即是,这个图中的环并不一定是从头开始,即(1,1)可能并不在循环节中:
知道以上结论后,我们可以将之上的理论进行代码实现:
#include
using namespace std;
const int N = 10;
const int M = 50;
int fir[N][N], ans[M];
int main()
{
int a, b, n;
while(scanf("%d%d%d", &a, &b, &n), a+b+n){
int x, y, cnt;
x = y = cnt = 1;
if(n == 1 || n == 2){
printf("1\n");
continue;
}
memset(fir, 0, sizeof(fir));
memset(ans, 0, sizeof(ans));
while(!fir[x][y]){
fir[x][y] = cnt;
ans[cnt++] = y;
int v = (a*y + b*x)%7;
x = y;
y = v;
}
--n;
if(n < fir[x][y]) printf("%d\n", ans[n]);
else{
int loop = cnt - fir[x][y];
n -= fir[x][y];
n %= loop;
printf("%d\n", ans[n+fir[x][y]]);
}
}
return 0;
}
即用fir数组来保存某一个值对第一次出现的计数,并为了我们方便地得到答案,使用 ansi a n s i 保存第i个答案。其中fir[x][y]保存的就是循环节的前导部分,loop为循环节长度:
此题还有另外一种解法,即 O(logn) O ( log n ) 的矩阵快速幂的算法。
做过用矩阵快速幂求斐波那契数列的第n项的同学会知道,有下式:
对于二次递推 f(n)=A∗f(n−1)+B∗f(n−2)+C f ( n ) = A ∗ f ( n − 1 ) + B ∗ f ( n − 2 ) + C ,有:
#include
using namespace std;
const int N = 2;
struct Mat{
int a[N][N];
Mat(){memset(a, 0, sizeof(a));}
Mat operator * (Mat& rh){
Mat ret;
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++)
for(int k = 0; k < N; k++){
ret.a[i][j] += a[i][k] * rh.a[k][j];
ret.a[i][j] %= 7;
}
return ret;
}
};
Mat powMod(Mat a, int e){
Mat ret;
for(int i = 0; i < N; i++) ret.a[i][i] = 1;
for(; e; e>>=1){
if(e&1) ret = ret*a;
a = a*a;
}
return ret;
}
int main()
{
int a, b, n;
while(scanf("%d%d%d", &a, &b, &n), a+b+n){
Mat ans;
ans.a[0][0] = a%7; ans.a[0][1] = b%7; ans.a[1][0] = 1;
if(n<=2){
printf("1\n");
continue;
}
ans = powMod(ans, n-2);
printf("%d\n", (ans.a[0][0]+ans.a[0][1])%7);
}
return 0;
}