BZOJ3240[Noi2013]矩阵游戏——矩乘优化递推+费马小定理

Description

婷婷是个喜欢矩阵的小朋友,有一天她想用电脑生成一个巨大的n行m列的矩阵(你不用担心她如何存储)。她生成的这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的元素,则F[i][j]满足下面的递推式:

F[1][1]=1
F[i,j]=a*F[i][j-1]+b (j!=1)
F[i,1]=c*F[i-1][m]+d (i!=1)
递推式中a,b,c,d都是给定的常数。

现在婷婷想知道F[n][m]的值是多少,请你帮助她。由于最终结果可能很大,你只需要输出F[n][m]除以1,000,000,007的余数。

Input

一行有六个整数n,m,a,b,c,d。意义如题所述

Output

包含一个整数,表示F[n][m]除以1,000,000,007的余数

Sample Input

3 4 1 3 2 6
Sample Output

85
HINT

样例中的矩阵为:

1 4 7 10

26 29 32 35

76 79 82 85

1<=N,M<=10^1000 000,a<=a,b,c,d<=10^9


这道题给出了递推式,但是棘手的是数据范围,居然达到了变态的10^1000000,所以肯定要用一些方法加速。
加速的方法就是使用矩阵。我们先看每一行:F[i][j]=a*F[i][j-1]+b (j!=1),那么我们设一个初始的二阶矩阵 A = ( 1 1 0 0 ) 表示答案,那么我们要乘的矩阵是 B = ( a 0 b 1 ) 。这个可以根据矩阵乘法的定义得到,因为A*B= ( a + b 1 0 0 ) ,再*B就是 ( a ( a + b ) + b 1 0 0 ) 。所以 A B n A [ 0 ] [ 0 ] = f [ 1 ] [ n + 1 ] 。(这里矩阵从0开始)
由于F[i][1]=c*F[i-1][m]+d (i!=1),所以我们要将 A B m 1 ,然后乘上矩阵 C = ( c 0 d 1 ) ,那么就会得到 f [ i + 1 ] [ 1 ] ,而要求 f [ n ] [ m ] 相当于就是 A ( B m 1 C ) n 1 B m 1 (因为最后不用乘C),于是我么直接复杂度 O ( l o g 2 N l o g 2 M ) ,发现n,m如此巨大,依旧过不去。所以我们要对n,m取模。具体的方法及证明我在这篇博客里写过,简单的说就是将n,m模(1000000007-1)即可。
但是还有一个问题,就是当a=1或c=1时,这个模就不适用了(费马小定理于此就不适用了),因为变成了现行的 + b + d ,显然,线性加时,n、m应该模(1000000007)即可。
#include<bits/stdc++.h>
#define MD 1000000007
#define ll long long
using namespace std;
ll read(){
    char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll mo(string s,int mm){
    ll d=0;
    for(ll i=0;i<s.size();i++) d=(d*10+(s[i]-'0'))%mm;
    return d;
}
string N,M;
ll n,m,a,b,c,d;
ll matr[2][2],A[2][2],B[2][2],D[2][2],I[2][2],U[2][2],C[2][2];
void mult(ll a[][2],ll b[][2]){
    for(ll i=0;i<2;i++)
     for(ll j=0;j<2;j++){
        ll res=0;
        for(ll k=0;k<2;k++) res=(res+a[i][k]*b[k][j])%MD;
        U[i][j]=res;
     }  
    for(ll i=0;i<2;i++)
     for(ll j=0;j<2;j++) a[i][j]=U[i][j];
}
int main()
{
    cin>>N>>M;
    a=read();b=read();c=read();d=read();
    if(a==1) n=mo(N,MD);else n=mo(N,MD);
    if(c==1) m=mo(M,MD);else m=mo(M,MD-1);m--;n--;
    matr[0][0]=matr[0][1]=1;matr[1][1]=matr[1][0]=0;
    A[0][0]=a;A[1][0]=b;A[0][1]=0;A[1][1]=1;
    B[0][0]=I[1][1]=c;B[1][0]=I[1][0]=d;B[0][1]=I[0][1]=0;B[1][1]=I[0][0]=1;I[1][0]*=-1;
    D[0][0]=D[1][1]=1;D[0][1]=D[1][0]=0;
    while(m){
        if(m&1) mult(D,A);
        mult(A,A);m>>=1;
    }
    C[0][0]=D[0][0];C[0][1]=D[0][1];C[1][0]=D[1][0];C[1][1]=D[1][1];mult(D,B);
    while(n){
        if(n&1) mult(matr,D);
        mult(D,D);n>>=1;
    }
    mult(matr,C);
    printf("%lld",matr[0][0]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/stevensonson/article/details/80903914