hdu 1757 (矩阵快速幂) 一个简单的问题 一个简单的开始

题目链接: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 }

 

转载于:https://www.cnblogs.com/JJCHEHEDA/p/4662227.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值