首页 > 代码库 > 矩阵乘法
矩阵乘法
p1353:
一个由自然数组成的数列按下式定义:
对于i <= k:ai = bi
对于i > k: ai = c[1]*a[i-1] + c[2]*a[i-2] + ... + c[k]*a[i-k]
其中bj 和 cj (1<=j<=k)是给定的自然数。写一个程序,给定自然数m <= n,
计算a[m] + a[m+1] + a[m+2] + ... + a[n], 并输出它除以给定自然数p的余数的值
矩阵乘法应用入门题,设矩阵A={a1,a2,a3...an-1,an,Sn},然后再构造一个B矩阵,使A*B=A’,A’={a2,a3,a4...an,an+1,Sn+1};用快速幂优化,求Sn,Sm-1,答案就出来了;
下面代码,因为是入门,所以也就没写什么struct Matrix;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<ctime> 7 #include<vector> 8 #include<algorithm> 9 #include<queue>10 using namespace std;11 #define LL long long12 const int maxn=20;13 LL k,mod,m,n;14 int a[maxn],b[maxn][maxn];15 int y[maxn][maxn],c[maxn];16 LL find(LL q){17 if(q<=0)return 0;18 memcpy(y,b,sizeof(y));19 int ans[maxn][maxn],t[maxn][maxn];20 memset(ans,0,sizeof(ans));21 memset(t,0,sizeof(t));22 for(int i=1;i<=k;i++)ans[i][i]=1;23 while(q!=0){24 if(q%2==1){25 memset(t,0,sizeof(t));26 for(int i=1;i<=k;i++)27 for(int j=1;j<=k;j++)28 for(int l=1;l<=k;l++)29 t[i][j]=((LL)t[i][j]+((LL)ans[i][l]*y[l][j])%mod)%mod;30 memcpy(ans,t,sizeof(t));31 }32 q/=2;33 memset(t,0,sizeof(t));34 for(int i=1;i<=k;i++)35 for(int j=1;j<=k;j++)36 for(int l=1;l<=k;l++)37 t[i][j]=(t[i][j]+((LL)y[i][l]*y[l][j])%mod)%mod;38 memcpy(y,t,sizeof(t));39 }40 LL sum=0;41 for(int i=1;i<=k;i++)sum=(sum+(LL)a[i]*ans[k][i]%mod)%mod;42 return sum;43 }44 int main(){45 cin>>k;46 for(int i=1;i<=k;i++)cin>>a[i];47 for(int i=1;i<k;i++)b[i][i+1]=1;48 for(int i=1;i<=k;i++)cin>>b[k][k-i+1];49 b[k+1][1]=1;50 b[k+1][k+1]=1;51 k++;52 cin>>m>>n>>mod;53 printf("%d\n",(find(n)-find(m-1)+mod)%mod);54 return 0;55 }
p1354:
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间
内,走过一定的距离。
但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。
又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多
少种散步的方法。
现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地
点A走到给定地点B共有多少条符合条件的路径;
这道题的原型是二维动归入门,然后这道题经历了以下变迁: 二维dp入门->二维dp(图论形式)->二维dp(矩阵乘法优化)->二维dp(矩阵乘法优化)+点边转化;
二维dp入门:f[t][i][j]=f[t-1][i-1][j]+f[t-1][i][j-1]+f[t-1][i+1][j]+f[t-1][i][j-1];很水;
二维dp(图论形式):建成图,然后f[t][i]=sum(f[t-1][j]+d[j][i]);
二维dp(矩阵乘法优化):t很大的时候,上矩阵乘法,建一个邻接矩阵,利用矩阵乘法转移;
这道题:由于不能走上一步走的路,点边转化一下,拆成两条有向边,其他和上面一样;
写得很难受,从来没在图上搞这么难受的设计,尤其是在不知道有没有效果的情况下;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<ctime> 7 #include<vector> 8 #include<algorithm> 9 #include<queue>10 using namespace std;11 #define LL long long12 const int maxn=125;13 const int mod=45989;14 struct node{15 int x,y,next;16 }e[maxn];17 int linkk[maxn],len=-1;18 int n,m,t,A,B;19 struct Matrix{20 int v[maxn][maxn];21 Matrix(){memset(v,0,sizeof(v));}22 friend Matrix operator*(const Matrix& a,const Matrix& b){23 Matrix c;24 for(int i=0;i<=len;i++)25 for(int j=0;j<=len;j++)26 for(int k=0;k<=len;k++)27 c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j]%mod)%mod;28 return c;29 }30 friend Matrix operator^(Matrix a,int b){31 Matrix c;32 for(int i=0;i<=len;i++)c.v[i][i]=1;33 while(b){34 if(b%2)c=c*a;35 b/=2;36 a=a*a;37 }38 return c;39 }40 }a,b;41 void insert(int x,int y){42 e[++len].y=y;e[len].next=linkk[x];linkk[x]=len;e[len].x=x;43 }44 void init(){45 memset(linkk,-1,sizeof(linkk));46 cin>>n>>m>>t>>A>>B;47 int x,y;A++,B++;48 for(int i=1;i<=m;i++){49 cin>>x>>y;50 x++,y++;51 insert(x,y);insert(y,x);52 }53 for(int i=linkk[A];i!=-1;i=e[i].next)a.v[1][i]++;54 for(int i=0;i<=len;i++)55 for(int j=0;j<=len;j++)56 if(e[i].y==e[j].x)if(i!=(j^1))b.v[i][j]++;57 a=a*(b^(t-1));58 LL sum=0;59 for(int i=linkk[B];i!=-1;i=e[i].next)sum=(sum+a.v[1][i^1])%mod;60 cout<<sum%mod<<endl;61 }62 int main(){63 init();64 }
p1355:
FJ的N(2 <= N <= 1,000,000)头奶牛选择了接力跑作为她们的日常锻炼项目。至于进行接力跑的地点,自然是在牧场中现有的T(2 <= T <= 100)条跑道上。
农场上的跑道有一些交汇点,每条跑道都连结了两个不同的交汇点I1_i和I2_i(1 <= I1_i <= 1,000; 1 <= I2_i <= 1,000)。每个交汇点都是至少两条跑道的端点。奶牛们知道每条跑道的长度length_i(1 <= length_i <= 1,000),以及每条跑道连结的交汇点的编号。并且,没有哪两个交汇点由两条不同的跑道直
接相连。你可以认为这些交汇点和跑道构成了一张图。
为了完成一场接力跑,所有N头奶牛在跑步开始之前都要站在某个交汇点上(有些交汇点上可能站着不只1头奶牛)。当然,她们的站位要保证她们能够将接力棒顺次传递,并且最后持棒的奶牛要停在预设的终点。
你的任务是,写一个程序,计算在接力跑的起点(S)和终点(E)确定的情况下,奶牛们跑步路径可能的最小总长度。显然,这条路径必须恰好经过N条跑道.
这道题和上道题的转移方程差不多:f[t][i]=min(f[t-1][j]+d[j][i]);
但不同的是上一题是sum,这一题是min。
和上题一样,建立矩阵,但矩阵的乘法规则换成c[i][j]=min(a[i][k]+b[k][j]);
然后就可以转移了,初始矩阵^n表示走n步后的最短长度;
在看题解之前还真不知道矩阵乘法的规则可以这么改变;
#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cstdlib>#include<ctime>#include<vector>#include<algorithm>#include<queue>using namespace std;#define LL long longconst int maxn=1010;const int inf=1000000000;struct node1{ int x,y,v,next;}e[maxn];int linkk[maxn],len=0,n,N,m,S,E,top=0;struct node{int x,y,v;}b[maxn];bool flag[maxn];int q[maxn],f[maxn];void insert(int x,int y,int v){ e[++len].y=y;e[len].x=x; e[len].next=linkk[x]; linkk[x]=len; e[len].v=v;}void init(){ scanf("%d%d%d%d",&n,&m,&S,&E); int x,y,v; for(int i=1;i<=m;i++){ scanf("%d%d%d",&v,&x,&y);b[i].x=x,b[i].y=y,b[i].v=v; if(!flag[x])q[++top]=x,flag[x]=1; if(!flag[y])q[++top]=y,flag[y]=1; } sort(q+1,q+top+1); for(int i=1;i<=maxn;i++)f[q[i]]=i; for(int i=1;i<=m;i++){ insert(f[b[i].x],f[b[i].y],b[i].v); insert(f[b[i].y],f[b[i].x],b[i].v); }}struct Mat{ LL v[202][202]; Mat(){memset(v,0,sizeof(v));} Mat operator*(Mat& x){ Mat c; memset(c.v,10,sizeof(c.v)); for(int i=1;i<=top;i++) for(int j=1;j<=top;j++) for(int k=1;k<=top;k++) { c.v[i][j]=min(c.v[i][j],v[i][k]+x.v[k][j]); } return c; } Mat operator^(int b){ Mat c; bool flag1=1; while(b){ if(b%2) { if(flag1)c=*this,flag1=0; else c=c*(*this); } b/=2; *this=(*this)*(*this); } return c; }}a;void work(){ memset(a.v,10,sizeof(a.v)); for(int x=1;x<=top;x++){ for(int i=linkk[x];i;i=e[i].next){ a.v[x][e[i].y]=e[i].v; } } a=a^(n); cout<<a.v[f[S]][f[E]]<<endl;}int main(){ init(); work();}
矩阵乘法有这么一段话:如果一个向量是其他向量的线性组合,便可以考虑用矩阵乘法;
这其实是在考验建模能力;
矩阵乘法