矩阵乘法略解
矩阵乘法
对于一个 的矩阵 和一个 的矩阵 ,有且仅有一个 的矩阵 使且对于 ,有 这就是矩阵乘法。比如:
题目描述
大家都知道 Fibonacci 数列吧,。
现在问题很简单,输入 和 ,求 。
输入格式
输入 。
输出格式
输出 。
样例输入
5 1000
样例输出
5
数据范围与提示
对于 的数据, 。
考虑使用矩阵快速幂优化时间复杂度。
若矩阵 使则
而可以使用快速幂求出。
下面推导 。 由矩阵乘法知 是一个 的矩阵,且
同理得
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[3][3];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",a[1][2]);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=2;s.a[1][1]=s.a[1][2]=1;
t.x=2;t.y=2;t.a[1][1]=0;t.a[1][2]=t.a[2][1]=t.a[2][2]=1;
ans=T(s,QT(t,n-2));
ans.pt();
}
题目描述
大家都知道 Fibonacci 数列吧,。
求的值。
样例输入
5 1000
样例输出
12
数据范围与提示
对于 的数据, 。
对于Fibonacci数列前 项和 ,显然有 。类似的,我们可以构造一矩阵 ,使 ,就能完成此题。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[4][4];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",a[1][3]);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=3;s.a[1][1]=s.a[1][2]=1;s.a[1][3]=2;
t.x=3;t.y=3;t.a[1][1]=t.a[3][1]=t.a[3][2]=0;t.a[1][2]=t.a[1][3]=t.a[2][1]=t.a[2][2]=t.a[2][3]=t.a[3][3]=1;;
ans=T(s,QT(t,n-2));
ans.pt();
}
题目描述
大家都知道 Fibonacci 数列吧,。
求 的值。
样例输入
5 5
样例输出
4
数据范围与提示
对于 的数据, 。
考虑构造一 矩阵
使
其中 是一个 的矩阵。同上文,我们有
那么, 怎么用形如上式的表达式表示呢?
事实上,我们无法用形如上式的表达式表示 ,因为我们无法用方阵 的若干次幂的形式表示 。
考虑如何将 表示成一次递推式。
得
得
设 ,于是我们得到 的递推关系式
所以我们可以构造矩阵
使
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[5][5];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",(a[1][2]*n%m-a[1][1]+m)%m);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=4;s.a[1][2]=s.a[1][3]=s.a[1][4]=1;
t.x=4;t.y=4;t.a[1][1]=t.a[2][1]=t.a[2][2]=t.a[3][2]=t.a[3][3]=t.a[4][3]=t.a[3][4]=1;
ans=T(s,QT(t,n-1));
ans.pt();
}
题目描述
原题来自:SCOI 2009
Windy 在有向图中迷路了。 该有向图有 个节点,Windy 从节点 出发,他必须恰好在 时刻到达节点 。
现在给出该有向图,你能告诉 Windy 总共有多少种不同的路径吗?
注意:Windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
输入格式
第一行包含两个整数 ,;
接下来有 行,每行一个长度为 的字符串。第 行第 列为 表示从节点 到节点 没有边,为 到 表示从节点 到节点 需要耗费的时间。
输出格式
包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以 的余数。
样例输入 1
2 2
11
00
样例输出 1
1
样例输入 2
5 30
12045
07105
47805
12024
12345
样例输出 2
852
数据范围与提示
对于 的数据,满足 。
的规模和边权都很小,考虑拆点,则时间复杂度为 。
如何把 优化成 呢?观察输入格式,我们很自然地想到矩阵,尝试使用矩阵快速幂优化。
设从 到 的边权为 。考虑当边权为 或 时,从 到 路径长为 的路径数
如果构造矩阵
则
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,t;
const int m=2009;
char str[20][20];
struct node{
ll a[110][110];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
}s,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
node c;c.x=n;c.y=n;
for(reg int i=1;i<=n;++i)
c.a[i][i]=1;
while(b){
if(b&1) c=T(c,a);
a=T(a,a);b>>=1;
}
return c;
}
int main(){
scanf("%d%d",&n,&t);
for(reg int i=1;i<=n;++i){
scanf("%s",str[i]+1);
for(reg int j=1;j<=n;++j)
str[i][j]-='0';
}
for(reg int i=1;i<=n;++i){
for(reg int j=1;j<=8;++j)
s.a[(i-1)*9+j][(i-1)*9+j+1]=1;
for(reg int j=1;j<=n;++j)
if(str[i][j])
s.a[(i-1)*9+str[i][j]][(j-1)*9+1]=1;
}
n*=9;s.x=s.y=n;
ans=QT(s,t);
printf("%d",ans.a[1][n-8]);
}
题目描述 情书
求
的值,其中 有
输入描述
一行 个整数,分别为 ,相邻两数以一个空格隔开。
输出描述
一行,一个整数,表示答案。
输入样例
输出样例
数据规模与约定
对于 的数据,有 ,其中 。
Pt.1 求 的值
仿照例2知, 是矩阵 的两个元素,请读者自己尝试推导。
Pt.2 求
简单等比数列求和。
设
则
得
使用乘法逆元和快速幂即可完成求解。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
struct node{
int x,y;
ll a[5][5];
node(){
memset(a,0,sizeof(a));
x=y=0;
}
}s,t;
ll a,b,c;
ll n,m,x,y,son;
ll sum=0;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
void exgcd(ll A,ll B,ll&x,ll&y){
if(!B){
x=1;y=0;
return;
}
exgcd(B,A%B,x,y);
ll t=x;x=y;y=t-A/B*y;
}
ll qt(ll a,int b){
if(b==1) return a;
ll e=qt(a,b/2)%m;
if(b%2)
return (e*e)%m*a%m;
else
return (e*e)%m;
}
int main(){
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&n,&m,&s.a[1][1],&s.a[1][2]);c%=m;
s.a[1][3]=s.a[1][1]+s.a[1][2];s.a[1][4]=s.a[1][1];
s.x=1;s.y=4;t.x=t.y=4;
t.a[2][1]=t.a[3][3]=t.a[3][4]=t.a[4][4]=1;
t.a[1][2]=t.a[1][3]=b;t.a[2][2]=t.a[2][3]=a;
s=T(s,QT(t,n-2));
exgcd(c-1,m,x,y);son=(qt(c,n+1)-c+m)%m;x=(x%m+m)%m;
printf("%lld",(((s.a[1][3]*n)%m-s.a[1][4]+m)%m+(x*son)%m+m)%m);
}

浙公网安备 33010602011771号