[Luogu P1397] [BZOJ 3240] [NOI2013]矩阵游戏

洛谷传送门
BZOJ传送门

题目描述

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

F[1][1]=1 F [ 1 ] [ 1 ] = 1

F[i,j]=aF[i][j1]+b(j1) F [ i , j ] = a ∗ F [ i ] [ j − 1 ] + b ( j ≠ 1 )

F[i,1]=cF[i1][m]+d(i1) F [ i , 1 ] = c ∗ F [ i − 1 ] [ m ] + d ( i ≠ 1 )

递推式中 a,b,c,d a , b , c , d 都是给定的常数。

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

输入输出格式

输入格式:

输入文件matrix.in包含一行有六个整数 n,m,a,b,c,d n , m , a , b , c , d 。意义如题所述。

输出格式:

输出文件matrix.out包含一个整数,表示 F[n][m] F [ n ] [ m ] 除以 1,000,000,007 1 , 000 , 000 , 007 的余数。

输入输出样例
输入样例#1:
3 4 1 3 2 6
输出样例#1:
85

说明

【样例1说明】

样例中的矩阵为:

1 4 7 10

26 29 32 35

76 79 82 85

数据范围

img

解题分析

听说很多人用矩阵水过了, 都是卡常dalao%%%%%%…

蒟蒻用的是推式子的方法, 总复杂度为 O(lgN) O ( l g N )

我们先考虑横排的递推, 看一下这个递推式:

F[i][2]=F[i][1]×a+b F [ i ] [ 2 ] = F [ i ] [ 1 ] × a + b

发现这是可以通项算第 M M 项的, 具体而言第M项为:
F[i][M]=F[i][1]×aM1+b×aM11a1 (a1) F [ i ] [ M ] = F [ i ] [ 1 ] × a M − 1 + b × a M − 1 − 1 a − 1   ( a ≠ 1 )

F[i][M]=F[i][1]+b×(M1) (a=1) F [ i ] [ M ] = F [ i ] [ 1 ] + b × ( M − 1 )   ( a = 1 )

考虑推至 F[i+1][1] F [ i + 1 ] [ 1 ] , 可得:

F[i+1][1]=F[i][1]×c×aM1+b×c×aM11a1+d (a1) F [ i + 1 ] [ 1 ] = F [ i ] [ 1 ] × c × a M − 1 + b × c × a M − 1 − 1 a − 1 + d   ( a ≠ 1 )

F[i+1][1]=F[i][1]×c+b×c×(M1)+d (a=1) F [ i + 1 ] [ 1 ] = F [ i ] [ 1 ] × c + b × c × ( M − 1 ) + d   ( a = 1 )

  • 如果 a1 a ≠ 1 , 则设 A=c×aM1 A = c × a M − 1 B=b×c×aM1a1+d B = b × c × a M − 1 a − 1 + d ,同样运用等比数列求和求通项,则有:

    F[i+1][1]=A×F[i][1]+B F [ i + 1 ] [ 1 ] = A × F [ i ] [ 1 ] + B

    F[N+1][1]=AN×F[i][1]+B×AN1A1 F [ N + 1 ] [ 1 ] = A N × F [ i ] [ 1 ] + B × A N − 1 A − 1

  • 如果 a=1 a = 1 , 则设 C=b×c×(M1)+d C = b × c × ( M − 1 ) + d ,则有:

    F[i+1][1]=c×F[i][1]+C F [ i + 1 ] [ 1 ] = c × F [ i ] [ 1 ] + C

    F[N+1][1]=F[i][1]+C×N (c=1) F [ N + 1 ] [ 1 ] = F [ i ] [ 1 ] + C × N   ( c = 1 )

    F[N+1][1]=cN×F[i][1]+C×cN1c1 (c1) F [ N + 1 ] [ 1 ] = c N × F [ i ] [ 1 ] + C × c N − 1 c − 1   ( c ≠ 1 )

这样我们就求出 F[N+1][1] F [ N + 1 ] [ 1 ] 的值, 要求 F[N][M] F [ N ] [ M ] 的值减去 d d 再乘上c的逆元即可。

还有一个问题, NM N 、 M 太大了怎么办? 我们发现 1000000007 1000000007 为质数, 而对于一个数 x x x1e9+72次方总是等于1(费马小定理), 所以我们处理质数上面的 NM N 、 M 时只需要疯狂 MOD1e9+6 M O D 1 e 9 + 6 即可。 在乘数上面的 NM N 、 M 更简单, 直接疯狂 MOD1e9+7 M O D 1 e 9 + 7 即可。

注意运算时随时取模。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define R register
#define W while
#define gc getchar()
#define IN inline
#define MX 1000500
#define ll long long
#define MOD 1000000007ll
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
}
ll ind, mod, a, b, c, d;
int len;
char buf[MX];
struct INFO
{ll ind, mod;} N, M;//ind为指数上的结果, mod为乘数上的结果
IN ll fpow(ll base, ll tim)
{
    ll ret = 1;
    for (R unsigned i = 0; i <= 31; ++i, base = base * base % MOD)
    if((1 << i) & tim) ret = ret * base % MOD;
    return ret;
}
IN void get(char *dat, INFO &x)
{
    for (R int i = 1; i <= len; ++i)
    {
        x.ind = (x.ind * 10 + dat[i] - 48) % (MOD - 1);
        x.mod = (x.mod * 10 + dat[i] - 48) %  MOD;
    }
}
IN ll inv(ll val) {return fpow(val, MOD - 2);}
int main(void)
{
    scanf("%s", buf + 1); len = std::strlen(buf + 1); get(buf, N);
    scanf("%s", buf + 1); len = std::strlen(buf + 1); get(buf, M);
    in(a), in(b), in(c), in(d);
    ll A, B, C, ans;
    if(a == 1)
    {
        C = (c * b % MOD * (M.mod - 1) % MOD + d) % MOD;
        if(c == 1) ans = (1 + N.mod * C % MOD) % MOD;
        else 
        {
            ll pw = fpow(c, N.ind);
            ans = (pw + C * (pw - 1 + MOD) % MOD * inv(c - 1)) % MOD;
        }
    }
    else
    {
        ll pw1 = fpow(a, (M.ind - 1 + MOD) % MOD);
        A = pw1 * c % MOD;
        B = (b * c % MOD * (pw1 + MOD - 1) % MOD * inv(a - 1) % MOD + d) % MOD;
        ll pw2 = fpow(A, N.ind);
        ans = pw2 + B * (pw2 + MOD - 1) % MOD * inv(A - 1) % MOD;
    }
    ans = (ans - d + MOD) % MOD * inv(c);
    printf("%lld", (ans % MOD + MOD) % MOD);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值