多项递推式—(循环节or矩阵快速幂)
点击进入原题👉:D: Starry的神奇魔法
Problem Description
啦啦啦,Starry正愉快的做着编程题,代码是多么优美呀!突然,有人问他一道数学题,对于数学渣渣的Starry来说,这是多么的烦恼呀。不过不要紧,他还可以问其他人的,所以他想到了聪明的你们。这个问题是由一个神奇的魔法师提出的,他创造了一种魔法,每次使用魔法可以让今天的魔力值为前一天魔力值的2018倍+前两天魔力值的8倍+前三天魔力值的12倍。当然,一天只能使用一次魔法,除了使用魔法外,每天魔法师还能产生8888的魔力值。第一、二、三天的初始魔力值为888。他想问的是第n天他可以拥有多少魔力值。
Input
第一行输入一个T表示有T个问题(1≤T≤104)。
接下来T行,每行有一个数n(1≤n≤1018),表示第n天。
Output
输出T行,每行输出第n天的魔力值,由于数很大,。结果对20180812取模。
Sample Input
3
2
5
8
Sample Output
888
17299052
16854116
题意:第一天888,第二天888,第三天888,之后递推式为:f(n)=(f(n-1)*2018+f(n-2)*8+f(n-3)*12+8888)%mod;
思路:多项递推式有两种写法(我指我所知道的0.0),分别是:“求循环节”和“矩阵快速幂优化递推式”。(大数据暴力肯定不行,所以别和我说暴力0.0)
循环节:(顾名思义f(n)到后面是有规律(循环的)):我们可以先试着去找一下这道题的循环节,打印前(20180813)天的,会发现这道题没有循环节。
所以我们使用第二种方法:
“矩阵快速幂优化递推式”:😀根据递推式:f(n)=(f(n-1)*2018+f(n-2)*8+f(n-3)*12+8888)%mod;得到以下矩阵:👇👇👇👇👇
化简一下:
所以我们最后要求的f(n)就可以知道了: 上方👆的这个矩阵(n-3)次放的结果为sum.m[4][4]
Tip:题目已经说了f(1),(2),f(3)为888
当(n<=3) f(n)=888;
所以当(n>3)时,f(n)=sum.m[0][0]*f(3)+sum.m[0][1]*f(2)+sum.m[0][2]*f(1)+sum.m[0][3]*1)%mod
所以当(n>3)时,f(n)=sum.m[0][0]*888+sum.m[0][1]*888+sum.m[0][2]*888+sum.m[0][3])%mod
所以我们这题就变成了求sum.m[4][4]这个矩阵,这里就要用的就是矩阵快速幂啦😀。下面直接上代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cmath> 4 #define mod 20180812 5 using namespace std; 6 struct mat{ 7 long long m[4][4];//这里开4*4就可以 8 }; 9 mat map,sum;//map指的是那个4*4原本的矩阵 10 //sum指的是map的(n-3)次放之后的矩阵 11 long long N; 12 mat muliply(mat a,mat b)//矩阵乘法 13 { 14 mat c; 15 memset(c.m,0,sizeof(c.m)); 16 for(long long i=0;i<4;i++) 17 for (long long j=0;j<4;j++) 18 for (long long z=0;z<4;z++) 19 { 20 c.m[i][j]+=a.m[i][z]*b.m[z][j]; 21 c.m[i][j]%=mod; 22 } 23 return c; 24 } 25 mat poww(mat a,long long n)//快速幂 26 { 27 memset(sum.m,0,sizeof(sum.m)); 28 for (long long i=0;i<4;i++)//单位矩阵 29 sum.m[i][i]=1; 30 while(n)//快速幂 31 { 32 if (n&1) 33 sum=muliply(sum,a); 34 a=muliply(a,a); 35 n>>=1; 36 } 37 return sum; 38 } 39 int main() 40 { 41 long long T; 42 scanf("%lld",&T); 43 while(T--) 44 { 45 scanf("%lld",&N); 46 map.m[0][0]=2018;map.m[0][1]=8;map.m[0][2]=12;map.m[0][3]=8888; 47 map.m[1][0]=1;map.m[1][1]=0;map.m[1][2]=0;map.m[1][3]=0; 48 map.m[2][0]=0;map.m[2][1]=1;map.m[2][2]=0;map.m[2][3]=0; 49 map.m[3][0]=0;map.m[3][1]=0;map.m[3][2]=0;map.m[3][3]=1; 50 if (N<=3) 51 { 52 printf("%lld\n",888); 53 } 54 else 55 { 56 sum=poww(map,N-3);//调用矩阵快速幂得到map的(n-3)次放的矩阵 57 printf("%lld\n",(sum.m[0][0]*888+sum.m[0][1]*888+sum.m[0][2]*888+sum.m[0][3])%mod); 58 //计算得到f(n) 59 } 60 } 61 return 0; 62 }
二:下面是一道之前写的简单的“循环节”题目和解析的博客链接👇https://www.cnblogs.com/bendandedaima/p/9323847.html