题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757
题意不难理解,当x小于10的时候,数列f(x)=x,当x大于等于10的时候f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10);
所求的是f(x)取m的模,而x,m,a[0]至a[9]都是输入项
初拿到这道题,最开始想的一般是暴力枚举,通过for循环求出f(x)然后再取模,但是有两个问题,首先f(x)可能特别大,其次是枚举超时。
所以,想到可以用到通过构造矩阵快速幂的方法。
稍微有点线性代数基础的人(比如本人)都知道可以构造一个10*10的矩阵
(10有点大,这里以3*3的代替,题意还是不变)
0 0 a0 0 0 a0 0 0 a0
(f0,f1,f2)* 1 0 a1 --->(f1,f2,f3) 然后(f0,f1,f2)* 1 0 a1 * 1 0 a1 --->(f2,f3,f4)
0 1 a2 0 1 a2 0 1 a2
0 0 a0 0 0 a0
最后一直到f(x) (f0,f1,f2)* 1 0 a1* 1 0 a1*******--->(fx-2,fx-1,fx)
0 1 a2 0 1 a2
10*10的矩阵也基本很这个一样,然后光有矩阵并没有太大的卵用,还得用快速幂来取模,只不过换成了矩阵而已。
先开两个二维数组把矩阵给存起来(对,开两个存一模一样的矩阵,如果一下子没有反应过来为什么的等你写到这里的时候就知道为什么了)
然后第三个数组存相乘过程中的结果。(如果快速幂不会的请百度)
最后的用已知的f(i)数组的前十项分别乘以最终矩阵的最后一列的和就是最终结果
(PS:加减乘法的取模可以分步,而除法不行)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 long long jz1[11][11],jz2[11][11],jz3[11][11],i,j,k; 6 long long a[11],m; 7 long long lsy(long long n) 8 { 9 long long sum=0; 10 for (i=0;i<=9;i++){ //创建矩阵 11 for (j=0;j<=9;j++){ 12 if (j!=9){ 13 if (i-j==1) 14 { 15 jz1[i][j]=1; 16 jz2[i][j]=1; 17 } 18 else 19 { 20 jz1[i][j]=0; 21 jz2[i][j]=0; 22 } 23 } 24 else 25 { 26 jz1[i][9]=a[9-i]; 27 jz2[i][9]=a[9-i]; 28 } 29 } 30 } 31 n-=10; 32 while (n!=0) //快速幂取模 33 { 34 if (n&1) 35 { 36 memset(jz3,0,sizeof(jz3)); 37 for (i=0;i<=9;i++){ 38 for (j=0;j<=9;j++){ 39 for (k=0;k<=9;k++){ 40 jz3[i][j]+=jz1[i][k]*jz2[k][j]%m; 41 } 42 } 43 } 44 memcpy(jz2,jz3,sizeof(jz3)); 45 } 46 memset(jz3,0,sizeof(jz3)); 47 for (i=0;i<=9;i++){ 48 for (j=0;j<=9;j++){ 49 for (k=0;k<=9;k++){ 50 jz3[i][j]+=jz1[i][k]*jz1[k][j]%m; 51 } 52 } 53 } 54 memcpy(jz1,jz3,sizeof(jz3)); 55 n>>=1; //不要忘了 56 } 57 for (i=0;i<10;i++) //求出f(x) 58 sum=(((i%m)*jz2[i][9])%m+sum)%m; 59 return sum; 60 } 61 int main() 62 { 63 long long n; 64 while (~scanf("%I64d %I64d",&n,&m)) 65 { 66 if (n==0&&m==0) break; 67 for (i=0;i<=9;i++) 68 scanf("%l64d",&a[i]); 69 if (n>=10) 70 printf("%I64d\n",lsy(n)); 71 else 72 printf("%I64d\n",n%m); 73 } 74 return 0; 75 }