矩阵快速幂

不到四个小时A5道题还是很开心的虽然题都很简单

今天做题时做到一道名为P哥破解密码的题。

猜想如果数据小的话可不可以用数位DP做

设f[i][j]表示到第i个字母,在末尾有j个A的方案数

推出以下式子:

f[i+1][0]=f[i][0]+f[i][1]+f[i][2]
f[i+1][1]=f[i][0]
f[i+1][2]=f[i][1]

因为(要求没有连续的三个A)不外乎三种情况:

1.当前末尾没有A(即这一位填的是B),那么前面的可能是1个A,2个A,或者也是B。

2.当前末尾一个A(即这一位填的是A且前一位是B),那么只能由前一位是B的转移过来。

3.当前末尾两个A(即这一位填的是A且前一位是A),那么只能由前一位是A的转移过来。

因为不出现连续的三个A,所以就没有其他的情况了。

推出式子后发现n是1e9的,线性推导会超时,然后就使用了矩阵快速幂来优化。

我突然发现我不会矩阵快速幂。。于是决定去打矩阵快速幂的板子。

搜到了矩阵快速幂的板子题,我突然发现我还没有打过矩阵乘法。。(弱的真实)

顺带提一嘴矩阵乘法,就是这个

ju mul(ju x,ju y)
{
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        c.m[i][j]=0;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        for(int p=1;p<=n;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
矩阵乘法

要求A矩阵的行数与B矩阵的列数相等,方便记忆的话C[i][j]=A的第i行乘B的第j列(但似乎有大佬说这种记忆方法极度肤浅)然后我就看到了这样的一段话

 

于是

矩阵快速幂

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long  
using namespace std;
struct ju{
    LL m[101][101];
}a,e,c;
LL n;
LL read()
{
    LL f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
ju mul(ju x,ju y)
{
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        c.m[i][j]=0;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        for(int p=1;p<=n;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
ju quick_pow(ju a,LL x)
{
    ju ans=e;
    while(x)
    {
        if(x&1)ans=mul(ans,a);
        a=mul(a,a);
        x>>=1;
    }
    return ans;
}
int main()
{
    n=read();LL k=read();
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        a.m[i][j]=read();
    for(int i=1;i<=n;++i)
        e.m[i][i]=1;//单位矩阵 
    ju ans=quick_pow(a,k);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
          printf("%lld ",ans.m[i][j]%mod);
        printf("
");
    }
} 
矩阵快速幂

矩阵加速

#include<bits/stdc++.h>
#define mod 1000000007
#define LL long long 
using namespace std;
struct ju{
    LL m[5][5];
}a,e,c;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
void init()
{
    a.m[1][1]=a.m[1][3]=a.m[2][1]=a.m[3][2]=1;//系数矩阵 
    for(int i=1;i<=3;++i)e.m[i][i]=1;//单位矩阵 
}
ju mul(ju x,ju y)
{
    memset(c.m,0,sizeof(c.m));
    for(int i=1;i<=3;++i)
      for(int j=1;j<=3;++j)
        for(int p=1;p<=3;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
ju quick_pow(ju a,LL x)
{
    ju ans=e;
    while(x)
    {
        if(x&1)ans=mul(ans,a);
        a=mul(a,a);
        x>>=1;
    }
    return ans;
}
int main()
{
    int T=read();
    while(T--)
    {
        int n=read();
        if(n<=3){printf("1
");continue;}
        init();
        ju ans=quick_pow(a,n-1);
        printf("%lld
",ans.m[1][1]);
    }
} 
/*
f[i-1]       f[i]
f[i-2] ----> f[i-1]
f[i-3]       f[i-2]
f[i] = f[i-1] * 1 + f[i-2] * 0 + f[i-3] * 1
f[i-1] = f[i-1] * 1 + f[i-2] * 0 + f[i-3] * 0
f[i-2] = f[i-1] * 0 + f[i-2] * 1 + f[i-3] * 0
so
1 0 1
1 0 0
0 1 0
*/
/*
矩阵(只看第一列)依次是:
1     1     2     3     4            
1     1     1     2     3
0     1     1     1     2(相当于是从f[2],f[1],f[0]这开始的) 
*/ 
矩阵加速

斐波那契数列

#include<bits/stdc++.h>
#define mod 1000000007 
#define LL long long 
using namespace std;
LL read()
{
    LL f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
struct ju{
    LL m[3][3];
}a,c,e;
void init()
{
    for(int i=1;i<=2;++i)e.m[i][i]=1;
    a.m[1][1]=a.m[1][2]=a.m[2][1]=1;
}
ju mul(ju x,ju y)
{
    memset(c.m,0,sizeof(c.m));
    for(int i=1;i<=2;++i)
      for(int j=1;j<=2;++j)
        for(int p=1;p<=2;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
ju quick_pow(ju a,LL x)
{
    ju ans=e;
    while(x)
    {
        if(x&1)ans=mul(ans,a);
        a=mul(a,a);
        x>>=1;
    }
    return ans;
}
int main()
{
    LL n=read();
    init(); 
    ju ans=quick_pow(a,n);
    printf("%lld
",ans.m[2][1]);
} 
斐波那契数列

斐波那契公约数

有一个很厉害的公式

gcd(f[a],f[b])=f[gcd(a,b)](证明摘自洛谷题解)

//gcd(f[a],f[b])=f[gcd(a,b)]
#include<bits/stdc++.h>
#define mod 100000000
#define LL long long
using namespace std;
struct ju{
    LL m[3][3];
}a,c,e;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
int gcd(int a,int b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
void init()
{
    a.m[1][1]=a.m[1][2]=a.m[2][1]=1;
    for(int i=1;i<=2;++i)e.m[i][i]=1;
}
ju mul(ju x,ju y)
{
    memset(c.m,0,sizeof(c.m));
    for(int i=1;i<=2;++i)
      for(int j=1;j<=2;++j)
        for(int p=1;p<=2;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
ju quick_pow(ju a,LL x)
{
    ju ans=e;
    while(x)
    {
        if(x&1)ans=mul(ans,a);
        a=mul(a,a);
        x>>=1;
    }
    return ans;
}
int main()
{
    int n=read(),m=read();
    int k=gcd(n,m);
    init();
    ju ans=quick_pow(a,k);
    printf("%lld
",ans.m[2][1]);
} 
斐波那契公约数

P哥破解密码

#include<bits/stdc++.h>
#define mod 19260817
#define LL long long 
using namespace std;
struct ju{
    LL m[5][5];
}a,c,e;
int read()
{
    int f=1,x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
void init()
{
    for(int i=1;i<=3;++i)e.m[i][i]=1;
    a.m[1][1]=a.m[1][2]=a.m[1][3]=a.m[2][1]=a.m[3][2]=1;
}
ju mul(ju x,ju y)
{
    memset(c.m,0,sizeof(c.m));
    for(int i=1;i<=3;++i)
      for(int j=1;j<=3;++j)
        for(int p=1;p<=3;++p)
          c.m[i][j]=(c.m[i][j]+x.m[i][p]*y.m[p][j]%mod)%mod;
    return c;
}
ju quick_pow(ju a,LL x)
{
    ju ans=e;
    while(x)
    {
        if(x&1)ans=mul(ans,a);
        a=mul(a,a);
        x>>=1;
    }
    return ans;
}
int main()
{
    int T=read();
    init();
    while(T--)
    {
        int n=read();
        ju ans=quick_pow(a,n);
        printf("%lld
",((ans.m[1][1]+ans.m[2][1])%mod+ans.m[3][1])%mod);
    }
} 
/*
f[i+1][0]=f[i][0]+f[i][1]+f[i][2]
f[i+1][1]=f[i][0]
f[i+1][2]=f[i][1]
*/
P哥破解密码

嗦不出话

原文地址:https://www.cnblogs.com/yyys-/p/11390988.html