文章目录
- 构造矩阵快速幂优化线性递推式
- POJ3734
- [Buses Gym - 101473H](https://vjudge.net/problem/Gym-101473H)
- [CF222E Decoding Genome](https://codeforces.com/problemset/problem/222/E)
构造矩阵快速幂优化线性递推式
遇到某些线性递推式,我们可以设法构造成矩阵乘积的形式,
像斐波那契数列 f[i]=f[i-1]+f[i-2] ,写成矩阵乘积的好处就是,
由于矩阵乘积具有结合律,我们可以用快速幂来优化。
(矩阵的构造一般不难,把转移矩阵写出来,左边的方阵很好求)
难点还是在于dp的递推式。
另外要注意,运算只要满足结合律,就可以使用快速幂,像max,min,加减乘除都可以。
举个max的例子:
转移方程很容易写出来:
但是这样时间复杂度是O(n * m * m)
我们重新定义矩阵乘法c[i][j]=max(a[i][k]+b[k][j])
,把递推式写成矩阵乘法的形式:
然后就可以用快速幂了。
POJ3734
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define re register
const int maxn = 1e4 + 10;
const int mx = 40;
const int mod = 10007;
const ll inf = 34359738370;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
//n个格子染色 4种颜色 求染完后1 3有偶数个的方案数
//a[i] b[i] c[i] 表示染完i个 1 3都为偶数 有一个是偶数 都是奇数
//a[i]=b[i-1]*1+a[i-1]*2
//b[i]=b[i-1]*2+c[i-1]*2+a[i-1]*2
//c[i]=c[i-1]*2+b[i-1]
//写成矩阵乘法的形式 用矩阵快速幂优化
//a[1]=2 b[1]=2 c[1]=0
struct matrix
{
int m[3][3];
void init()
{
memset(m,0,sizeof m);
m[0][0]=1;
m[1][1]=1;
m[2][2]=1;
}
inline matrix operator * (const matrix &f)const
{
matrix ans;
memset(ans.m,0,sizeof ans.m);
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
for(int k=0;k<3;k++)
ans.m[i][j]=(ans.m[i][j]+m[i][k]*f.m[k][j]%mod)%mod;
}
}
return ans;
}
}mat;
matrix ksm(matrix a,int n)
{
matrix ret;
ret.init();
while(n)
{
if(n&1) ret=ret*a;
a=a*a;
n>>=1;
}
return ret;
}
int n;
int main()
{
int t;cin>>t;
while(t--)
{
scanf("%d",&n);
mat.m[0][0]=2,mat.m[0][1]=1,mat.m[0][2]=0;
mat.m[1][0]=2,mat.m[1][1]=2,mat.m[1][2]=2;
mat.m[2][0]=0,mat.m[2][1]=1,mat.m[2][2]=2;
mat=ksm(mat,n-1);
int sum=mat.m[0][0]*2%mod+mat.m[0][1]*2%mod;
cout<<sum%mod<<'\n';
}
return 0;
}
Buses Gym - 101473H
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define re register
const int maxn = 1e4 + 10;
const int mx = 105;
const int mod = 1000000;
const ll inf = 34359738370;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
//车队由大车和小车组成 大车10米 小车5米 分别有B A种颜色可以涂 给定车队长度n 问所有可能的涂色方案数
//f[i]=a*f[i-5]+b*f[i-10]
//由于只有5的倍数才有用 n/=5 f[i]=a*f[i-1]+b*f[i-2]
//边界f[1]=a f[0]=1
//都是n a b都是1e15级别
//矩阵快速幂
struct matrix
{
ll mt[2][2];
void init()
{
memset(mt,0,sizeof mt);
for(int i=0;i<2;i++) mt[i][i]=1;
}
matrix operator *(const matrix &f)const
{
matrix ans;
memset(ans.mt,0,sizeof ans.mt);
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
ans.mt[i][j]=(ans.mt[i][j]+mt[i][k]*f.mt[k][j]%mod)%mod;
}
}
}
return ans;
}
}ei;//ei 转移矩阵
ll n,a,b;
matrix ksm(matrix x,ll q)
{
matrix ret;
ret.init();
while(q)
{
if(q&1) ret=ret*x;
x=x*x;
q>>=1;
}
return ret;
}
int main()
{
cin>>n>>a>>b;
a%=mod,b%=mod;
ei.mt[0][0]=a,ei.mt[0][1]=b;
ei.mt[1][0]=1,ei.mt[1][1]=0;
ei=ksm(ei,n/5-1);
ll ans=ei.mt[0][0]*a%mod+ei.mt[0][1]%mod;
printf("%06lld\n",ans%mod);
return 0;
}
CF222E Decoding Genome
某种DNA,长度不超过1e15,可以由'a'
到'z'
,'A'
到'Z'
52种字母组成,有k对(x,y)表示y不能出现在x的下一位,给出n m k,计算一共有多少种DNA序列。
首先不管n的范围想出一个dp递推式:
dp[n][j]表示长度为n,结尾是j字符的合法DNA数量
dp[n][j]=Σdp[n-1][k]*mp[k][j]
(枚举k,如果k后面能接j,mp[k][j]==1
,否则0)
这是一个线性的递推式,而且mp的范围不大,试试能否写成矩阵乘积的形式,
写出来发现是这样的:
最终结果就是左边矩阵元素之和,而由于初始矩阵每个元素都是1,所以我们求出转移矩阵的n-1次方,再把所有元素加起来就能得到最终答案。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define re register
const int maxn = 1e4 + 10;
const int mx = 105;
const int mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll n;
int m,k;
struct matr
{
ll ax[55][55];
void init()
{
memset(ax,0,sizeof ax);
for(int i=0;i<55;i++) ax[i][i]=1;
}
matr operator *(const matr &f)const
{
matr ans;
memset(ans.ax,0,sizeof ans.ax);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=m;k++)
{
ans.ax[i][j]=(ans.ax[i][j]+ax[i][k]*f.ax[k][j]%mod)%mod;
}
}
}
return ans;
}
}ei;
matr ksm(matr x,ll b)
{
matr ret;ret.init();
while(b)
{
if(b&1) ret=ret*x;
x=x*x;
b>>=1;
}
return ret;
}
inline int getid(char _)
{
if('a'<=_ && _<='z') return _-'a'+1;
else return _-'A'+27;
}
int mp[55][55];//不能连续出现的对
int main()
{
scanf("%lld %d %d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++) mp[i][j]=1;//初始都能出现
}
for(int i=1;i<=k;i++)
{
char s[5];scanf("%s",s);
int x=getid(s[0]),y=getid(s[1]);
mp[x][y]=0;
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
ei.ax[i][j]=mp[j][i];//转移矩阵ax是mp的转置
}
}
ei=ksm(ei,n-1ll);
ll ans=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++) ans=(ans+ei.ax[i][j])%mod;
}
cout<<ans<<'\n';
return 0;
}