(HDU)1005 -- Number Sequence(数列)

问题描述
数列定义如下:

f(1)= 1,f(2)= 1,f(n)=(A * f(n-1)+ B * f(n-2))mod 7。

给定A,B和n,你要计算f(n)的值。
 

输入
输入由多个测试用例组成。 每个测试用例在一行(1 <= A,B <= 10001 <= n <= 100,000,000)中包含3个整数A,B和n。三个零表示输入结束,此测试用例不进行处理。

 

输出
对于每个测试用例,在一行上输出f(n)的值。
 

样例输入
1 1 3
1 2 10
0 0 0
 

样例输出
2
5
问题

注意这道题n的范围,1≤n≤1000000000,要小心超时超内存的问题。

看到题目直接写往往会忽视很多细节问题,给出公式的题目注意看看有没有规律。

mod是取模的意思,这里可以认为是取余数。

所谓的同余,顾名思义,就是许多的数被一个数d去除,有相同的余数。d数学上的称谓为模。如a=6,b=1,d=5,则我们说a和b是模d同余的。因为他们都有相同的余数1。
       数学上的记法为:
       a≡ b(mod d)
       可以看出当n<d的时候,所有的n都对d同商,比如时钟上的小时数,都小于12,所以小时数都是模12的同商.
对于同余有三种说法都是等价的,分别为:
       (1) a和b是模d同余的.
       (2) 存在某个整数n,使得a=b+nd .
       (3) d整除a-b.
       可以通过换算得出上面三个说法都是正确而且是等价的.
       基本定律:
       同余公式也有许多我们常见的定律,比如相等律,结合律,交换律,传递律….如下面的表示:
       1)a≡a(mod d)
       2)对称性 a≡b(mod d)→b≡a(mod d)
       3)传递性 (a≡b(mod d),b≡c(mod d))→a≡c(mod d)
       如果a≡x(mod d),b≡m(mod d),则
           4)a+b≡x+m (mod d)
           5)a-b≡x-m (mod d)
           6)a*b≡x*m (mod d )
           7)a/b≡x/m (mod d)
       8)a≡b(mod d)则a-b整除d
       9)a≡b(mod d)则a^n≡b^n(mod d)
      10)如果ac≡bc(mod m),且c和m互质,则a≡b(mod m)
      模运算的运算规则:
      (a + b)  mod  p = (a  mod  p + b  mod  p)  mod  p            (1)
      (a - b)  mod  p = (a  mod  p - b  mod  p)  mod  p              (2) 
      (a * b)  mod  p = (a  mod  p * b  mod  p)  mod  p              (3)
      a^b  mod  p = ((a  mod  p)^b)  mod  p                              (4)
      结合率: ((a+b)  mod  p + c)  mod  p = (a + (b+c)  mod  p)  mod  p (5)
                     ((a*b)  mod  p * c) mod  p = (a * (b*c)  mod  p)  mod  p     (6)
      交换率: (a + b)  mod  p = (b+a)  mod  p                 (7)
                     (a * b)  mod  p = (b * a)  mod  p                 (8)
      分配率: ((a +b) mod  p * c)  mod  p = ((a * c)  mod  p + (b * c)  mod  p)  mod  p (9)
      重要定理:若a≡b ( mod  p),则对于任意的c,都有(a + c) ≡ (b + c) ( mod p);(10)
                        若a≡b ( mod  p),则对于任意的c,都有(a * c) ≡ (b * c) ( mod p);(11)
                        若a≡b ( mod  p),则对于任意的c,都有ac≡ bc ( mod p);     (13)

本文地址:http://blog.csdn.net/a359680405/article/details/41675143
了解内容:模运算性质

对于公式 f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7,为了防止溢出,改成 f(n) = (A mod 7 * f(n - 1) + B mod 7* f(n - 2))mod 7

(如果看不懂改写原因就看一下上面的了解内容,或者自己写几个例子验证。这题A和B范围比较小,其实不改也可以。)

f(n)自身是保证在0-6的整数范围内的,同样f(n-1)和f(n-2)只有这七种取值。

f(n)是由f(n-1)和f(n-2)确定的,因为A和B是一个确定的值,对整个公式没有影响。

用映射的思想来看,一对f(n-1)和f(n-2)只能映射一个f(n),

所以f(n)的计算 最多 只有 49种可能,如果你还是理解不了,看下面的表格...

假设A=2,B=1...但是循环周期不是49(这是最糟糕的情况)。

fn f(n-1)
0 1 2 3 4 5 6
f(n-2) 0 0 2 4 6 1 3 5
1 1 3 5 0 2 4 6
2 2 4 6 1 3 5 0
3 3 5 0 2 4 6 1
4 4 6 1 3 5 0 2
5 5 0 2 4 6 1 3
6 6 1 3 5 0 2 4

如果有连续的两项,在前面的数列中出现过,最小循环节就找到了(注意不一定是出现连续两个1,1的时候,即出现f(1)和f(2))

eg:1  1  3  4  1  6  1  3(1和3在前面出现过了,最小循环节就是13416)

找到了最小循环节,代码就容易写出来了。

 

下面的写法是直接将49个数作为循环节,但是不是最小循环节(周期函数不是有最小正周期嘛)。

 1 #include <iostream>  
 2 using namespace std;  
 3 int arr[50];  
 4 int main()  
 5 {  
 6     int n,a,b;  
 7     arr[1]=arr[2]=1;  
 8     while(cin>>a>>b>>n)  
 9     {  
10         if(a==0&&b==0&&n==0)  
11             break;  
12         int minn=n<50?n:50;//一个小小的优化  
13         for(int i=3; i<=minn; i++)  
14         {  
15             arr[i]=(a*arr[i-1]+b*arr[i-2])%7;  
16         }  
17         cout<<arr[n%49]<<endl;  
18   
19     }  
20     return 0;  
21 }  
不寻找最小循环节,直接用49作为周期的写法

这个写法比较麻烦,但是也算一种拓展:

 1 #include <cstring>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 using namespace std;
 5 
 6 int rec[60];
 7 
 8 int main()
 9 {
10     int a, b, n;
11     rec[0] = rec[1] = rec[2] = 1;
12     while( scanf( "%d %d %d", &a, &b, &n ), a | b | n )
13     {
14         int beg, end, flag = 0;
15         for( int i = 3; i <= n && !flag; ++i )
16         {
17             rec[i] = ( a * rec[i-1] + b * rec[i-2] ) % 7;
18             for( int j = 2; j <= i - 1; ++j )
19             {
20                 if( rec[i] == rec[j] && rec[i-1] == rec[j-1] )
21                 {
22                     beg = j, end = i;
23                     flag = 1;
24                     break;
25                 }
26             }
27         }
28         if( flag )
29         {
30             printf( "%d\n", rec[beg+(n-end)%(end-beg)] );
31         }
32         else
33             printf( "%d\n", rec[n] );
34     }
35     return 0;
36 }
寻找最小循环节

 

posted @ 2016-12-01 21:16  ACDoge  阅读(395)  评论(0编辑  收藏  举报