矩阵快速幂
不到四个小时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("\n"); } }
#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\n");continue;} init(); ju ans=quick_pow(a,n-1); printf("%lld\n",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\n",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\n",ans.m[2][1]); }
#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\n",((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] */
嗦不出话