zoukankan      html  css  js  c++  java
  • (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
    ", rec[beg+(n-end)%(end-beg)] );
    31         }
    32         else
    33             printf( "%d
    ", rec[n] );
    34     }
    35     return 0;
    36 }
    寻找最小循环节
  • 相关阅读:
    ajax调接口示例
    JQuery的ready函数与JS的onload的区别详解
    DIV拖拽
    Lasso估计学习笔记(二)
    Lasso估计论文学习笔记(一)
    ubuntu下部署mongodb以及设置允许远程连接
    C#获取Honeywell voyager 1400g扫码后的数据
    vs2015“当前不会命中断点 还没有为该文档加载任何符号”的解决方法
    pyqt4 python2.7 中文乱码的解决方法
    使用pip 提示UnicodeDecodeError: 'ascii' codec can't decode解决方法
  • 原文地址:https://www.cnblogs.com/ACDoge/p/6123608.html
Copyright © 2011-2022 走看看