Problem Description
Farmer John有n头奶牛.
某天奶牛想要数一数有多少头奶牛,以一种特殊的方式:
第一头奶牛为1号,第二头奶牛为2号,第三头奶牛之后,假如当前奶牛是第n头,那么他的编号就是2倍的第n-2头奶牛的编号加上第n-1头奶牛的编号再加上自己当前的n的三次方为自己的编号.
现在Farmer John想知道,第n头奶牛的编号是多少,估计答案会很大,你只要输出答案对于123456789取模.
Input
第一行输入一个T,表示有T组样例
接下来T行,每行有一个正整数n,表示有n头奶牛 (n>=3)
其中,T=10^4,n<=10^18
Output
共T行,每行一个正整数表示所求的答案
Sample Input
5 3 6 9 12 15
Sample Output
31 700 7486 64651 527023
根据题目条件,易知递推公式:,但是由于题给数据特别巨大,所以不能用数组和简单for循环递推来完成这个目标。
所以就有另一种办法——矩阵代数,利用矩阵之间的乘积关系来表达出这个式子,并且结果矩阵中必定含有所求项(在这里是A(n))。不过我们现在除了知道思路的大方向之外什么都不知道,解决这个问题需要攻破两个点:
- 怎么用矩阵乘积表达这个递推式?
- 表达的话,各个矩阵都是什么样子?
首先,依据下表(盗的图)来解决第一个问题。
第一个问题的答案在表中很明显,所谓第A(n)项就是一个特殊矩阵的幂和原矩阵的乘积。
用矩阵解决递推问题的这种思路是怎么得出来的我不知道,但是可以从上表知道一件事情来解决第二个问题:
通过把代表原递推式A(n-1)的矩阵乘以一个X矩阵或者X向量就可以得到代表递推式第A(n)项的矩阵,简而言之,这是一个矩阵方程,而未知量X即是我们的“特殊矩阵”的表达式。
令人欣喜的是,另外两项也是有规律可循的——A(n-1)矩阵中包含递推项A(n-1)与A(n-2)以及递推式常数(n)从其给定幂次到0次幂(也就是常数1)的罗列,由于这个矩阵是用来求第n项的,所以A(n-1)项的矩阵里的常数是n.右侧A(n)矩阵就是把左侧A(n-1)矩阵里的递推项下标和常数加上1即可。
详情可见上表的递推式。
而初始矩阵就更好求了,就是把题目中的初始数据依次代入即可。
那么就可以得到这样的两个重要式子:
1.
2.初始矩阵:{2,1,27,9,3,1}(6 x 1矩阵)(若方程求解有问题,欢迎小窗提问)
用实现这样的操作还需要模拟矩阵乘法,在此不作赘述。
综上所述,可以得到这样的思路
- 根据题目中的信息,得到递推式,并用矩阵表示第n项
- 根据递推式,写出一个矩阵方程:原递推式n-1项乘以一个未知的x等于第n项,解出未知的x矩阵(或向量),并解出初始矩阵
利用模拟的矩阵乘法和矩阵幂乘,解决问题。
代码如下:
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <algorithm>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf("%lld",&a)
#define din(a) scanf("%d",&a)
#define printlnlld(a) printf("%lld\n",a)
#define printlnd(a) printf("%d\n",a)
#define printlld(a) printf("%lld",a)
#define printd(a) printf("%d",a)
#define reset(a,b) memset(a,b,sizeof(a))
const int INF=0x3f3f3f3f;
using namespace std;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod=123456789;
///Schlacht von Stalingrad
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
struct matrix//为了避免传参,把表示矩阵的二维数组塞进结构体里
{
ll mat[8][8];
ll row,col;
};
matrix multiplication(matrix a,matrix b)//根据行列规则模拟的矩阵乘法
{
matrix tmp;
reset(tmp.mat,0);
tmp.row=a.row;
tmp.col=b.col;
if(a.col==b.row)
{
for(int i=1; i<=a.row; i++)
{
for(int j=1; j<=b.col; j++)
for(int k=1; k<=a.col; k++)
{
tmp.mat[i][j]+=(a.mat[i][k]*b.mat[k][j])%mod;
tmp.mat[i][j]%=mod;
}
}
}
return tmp;
}
matrix quick_power(matrix a,ll n)
{
matrix identity;//单位矩阵的定义与初始化
reset(identity.mat,0);
identity.col=6;
identity.row=6;
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++)
{
if(i==j)
identity.mat[i][j]=1;
else
identity.mat[i][j]=0;
}
while(n)//快速幂
{
if(n&1)
identity=multiplication(identity,a);
n>>=1;
a=multiplication(a,a);
}
// for(int i=1; i<=identity.row; i++)
// {
// for(int j=1; j<=identity.col; j++)
// cout<<identity.mat[i][j]<<" ";
// cout<<endl;
// }
return identity;
}
matrix initialed,ILETS;
void initialization()
{
initialed.col=1,initialed.row=6;
initialed.mat[1][1]=2,initialed.mat[2][1]=1;
initialed.mat[3][1]=27,initialed.mat[4][1]=9;
initialed.mat[5][1]=3,initialed.mat[6][1]=1;//初始矩阵
ILETS.col=6,ILETS.row=6;
ILETS.mat[1][1]=1,ILETS.mat[1][2]=2,ILETS.mat[1][3]=1;
ILETS.mat[2][1]=1,ILETS.mat[3][4]=3,ILETS.mat[3][3]=1;
ILETS.mat[3][5]=3,ILETS.mat[3][6]=1,ILETS.mat[4][4]=1;
ILETS.mat[4][5]=2,ILETS.mat[4][6]=1,ILETS.mat[5][6]=1;
ILETS.mat[6][6]=1;ILETS.mat[5][5]=1;//这是根据矩阵方程解出的X矩阵
for(int i=1; i<=6; i++)
for(int j=1; j<=6; j++)
{
if(!ILETS.mat[i][j])
ILETS.mat[i][j]=0;
}
}
int DETERMINATION()
{
cin.ios::sync_with_stdio(false);
ll t;
cin>>t;
initialization();
while(t--)
{
ll n;
cin>>n;
matrix ans,rmp;
ans=quick_power(ILETS,n-2);
rmp=multiplication(ans,initialed);
// for(int i=1;i<=6;i++)
// cout<<rmp.mat[i][1]<<endl;
cout<<rmp.mat[1][1]<<endl;//这一项就是答案
}
return 0;
}