HDU 6470 Count(数数) (矩阵快速幂与矩阵方程)

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

根据题目条件,易知递推公式:A(n)=A(n-1)+2*A(n-2)+n^{3},但是由于题给数据特别巨大,所以不能用数组和简单for循环递推来完成这个目标。

所以就有另一种办法——矩阵代数,利用矩阵之间的乘积关系来表达出这个式子,并且结果矩阵中必定含有所求项(在这里是A(n))。不过我们现在除了知道思路的大方向之外什么都不知道,解决这个问题需要攻破两个点:

  1. 怎么用矩阵乘积表达这个递推式?
  2. 表达的话,各个矩阵都是什么样子? 

首先,依据下表(盗的图)来解决第一个问题。 

第一个问题的答案在表中很明显,所谓第A(n)项就是一个特殊矩阵的幂和原矩阵的乘积。

用矩阵解决递推问题的这种思路是怎么得出来的我不知道,但是可以从上表知道一件事情来解决第二个问题:

A(n-1)*x((vector)or(matrix))=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.\begin{matrix} A(n) \\ A(n-1) \\ (n+1)^{3} \\ (n+1)^{2} \\ (n+1)^{1} \\ (n+1)^{0} \end{matrix} = \begin{matrix} A(n-1) \\ A(n-2) \\ n^{3} \\ n^{2} \\ n^{1} \\ n^{0} \end{matrix} * X

     2.初始矩阵:{2,1,27,9,3,1}(6 x 1矩阵)(若方程求解有问题,欢迎小窗提问)

用实现这样的操作还需要模拟矩阵乘法,在此不作赘述。

综上所述,可以得到这样的思路

  1. 根据题目中的信息,得到递推式,并用矩阵表示第n项
  2. 根据递推式,写出一个矩阵方程:原递推式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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43874261/article/details/89632355