[BZOJ3240][NOI2013]矩阵游戏(十进制快速幂+矩阵乘法 / 数学相关)

题目:

我是超链接

题解:

NOI老是考奇奇怪怪的数学题诶。。。

解法一:

F ( n , m ) = a · F ( n , m 1 ) + b

= a m 1 F ( n , 1 ) + b + a b + . . . + a m 2 b

= a m 1 c · F ( n 1 , m ) + a m 1 d + b + a b + . . . + a m 2 b

p = b + a b + . . . + a m 2 b

x = a m 1 c

y = a m 1 d + p

q = y + x y + . . . + x n 2 y

F ( n , m ) = x · F ( n 1 , m ) + y = x n 1 F ( 1 , m ) + q = x n 1 ( a m 1 + p ) + q

p,q都可以使用等比数列求和公式计算(注意若公比为1,不能使用求和公式)

代码:

#include <cstdio> 
#include <cstring>
using namespace std;
const int mod=1e9+7;
#define LL long long
const int N=1000005;
char stn[N],stm[N];
LL ksm(LL a,LL k,int mod)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
int main()
{
    freopen("matrixb.in","r",stdin);
    freopen("matrixb.out","w",stdout);
    LL n=0,n1=0,m=0,m1=0,a,b,c,d;scanf("%s",stn); scanf("%s",stm);
    int sb=strlen(stn),st=strlen(stm);
    for (int i=0;i<sb;i++) 
    {
        n=n*10+stn[i]-'0';
        if (n>=mod) n%=mod;
        n1=n1*10+stn[i]-'0';
        if (n1>=mod-1) n1%=(mod-1);
    }
    n=(n-1+mod)%mod;
    n1=(n1-1+mod-1)%(mod-1);
    for (int i=0;i<st;i++) 
    {
        m=m*10+stm[i]-'0';
        if (m>=mod) m%=mod;
        m1=m1*10+stm[i]-'0';
        if (m1>=mod-1) m1%=(mod-1);
    }
    m=(m-1+mod)%mod;
    m1=(m1-1+mod-1)%(mod-1);
    scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
    LL p,x,y,q,inv;
    if (a==1) p=b*m%mod;
    else inv=ksm(a-1,mod-2,mod),p=b*(ksm(a,m1,mod)-1)%mod*inv%mod;
    x=ksm(a,m1,mod)*c%mod;
    y=ksm(a,m1,mod)*d%mod+p;y%=mod;
    if (x==1) q=y*n%mod;
    else inv=ksm(x-1,mod-2,mod),q=y*(ksm(x,n1,mod)-1)%mod*inv%mod;
    LL ans=ksm(x,n1,mod)*(ksm(a,m1,mod)%mod+p)%mod+q;
    printf("%lld",ans%mod);
}

解法二:

解法一是数学做法,然而我们也可以用矩阵快速幂解决这个问题
设矩阵A3为

1 1

矩阵A1为

a 0
b 1

矩阵A2为

c 0
d 1

那么转移到n行m列应该是: A 3 A 1 m 1 ( A 2 A 1 m 1 ) n 1

对于指数上很大的数字,这是矩阵不能用费马小定理,可以使用十进制快速幂,这种方法就是若是求x的y次方,从低位到高位查看y十进制下的每一位,这一位为几,就让ans乘几个x,然后让 x = x 10 ,不断这样操作即可。

卡常数卡到怀疑人生= =
不用longlong会快,怕爆就*1LL;小的乘数不用快速幂。

代码:

#include <cstdio>
#include <cstring>
using namespace std;
const int mod=1e9+7;
const int N=1000005;
char n[N],m[N];
struct mat{int sq[5][5];}a1,a2;
mat operator *(mat a,mat b)
{
    mat c;
    c.sq[1][1]=(1LL*a.sq[1][1]*b.sq[1][1]%mod+1LL*a.sq[1][2]*b.sq[2][1]%mod)%mod;
    c.sq[1][2]=(1LL*a.sq[1][1]*b.sq[1][2]%mod+1LL*a.sq[1][2]*b.sq[2][2]%mod)%mod;
    c.sq[2][1]=(1LL*a.sq[2][1]*b.sq[1][1]%mod+1LL*a.sq[2][2]*b.sq[2][1]%mod)%mod;
    c.sq[2][2]=(1LL*a.sq[2][1]*b.sq[1][2]%mod+1LL*a.sq[2][2]*b.sq[2][2]%mod)%mod;
    return c;
}
mat ksm10(mat a,char *n)
{
    mat ans;
    ans.sq[1][1]=ans.sq[2][2]=1;
    ans.sq[1][2]=ans.sq[2][1]=0;
    int sb=strlen(n+1);
    for (int i=sb;i>=1;i--)
    {
        int x=n[i]-'0';mat tmp=a;
        if (n[i]=='9') tmp=tmp*tmp,tmp=tmp*tmp,tmp=tmp*tmp,tmp=tmp*a,ans=ans*tmp;
        else if (n[i]=='8') tmp=tmp*tmp,tmp=tmp*tmp,tmp=tmp*tmp,ans=ans*tmp;
        else for (int j=1;j<=n[i]-'0';j++) ans=ans*tmp;
        tmp=a;tmp=tmp*tmp,tmp=tmp*tmp,tmp=tmp*tmp,tmp=tmp*a,tmp=tmp*a;
        a=tmp;
    }
    return ans;
}
int main()
{
    scanf("%s",n+1); scanf("%s",m+1);
    scanf("%d%d%d%d",&a1.sq[1][1],&a1.sq[2][1],&a2.sq[1][1],&a2.sq[2][1]);
    int sb=strlen(n+1);
    for (int i=sb;i>=1;i--)
      if (n[i]=='0') n[i]='9';else {n[i]=n[i]-1; break;}
    sb=strlen(m+1);
    for (int i=sb;i>=1;i--)
      if (m[i]=='0') m[i]='9';else {m[i]=m[i]-1; break;}

    a1.sq[2][2]=1; a2.sq[2][2]=1;  

    a1=ksm10(a1,m);
    a2=ksm10(a2*a1,n);
    a1=a1*a2;
    printf("%d",(a1.sq[1][1]+a1.sq[2][1])%mod);
}

猜你喜欢

转载自blog.csdn.net/blue_cuso4/article/details/80519890