题目描述
题解
用朴素的矩阵快速幂的话时间无法承受。其实这道题的思路挺奇特的,不是自己想出来的很不爽。
这里有一个递推的思路:考虑k能不能从之前的状态转移过来。答案是肯定的。
当
k%2==0
时:
sum(k)=∑i=1kAk=(1+Ak2)∗(A+A2+…+Ak2)
当 k%2!=0 时
sum(k)=∑i=1kAk=A+(A+A⌊k2⌋)∗(A+A2+…+A⌊k2⌋)
也就是说先计算出 sum(⌊k2⌋) 的值就可以经过很少的计算得出 sum(k) 的值,那么我们可以不断递归下去。
有必要说明一点:第一个公式出现了1,它其实就代表了一个单位矩阵,即
I=⎡⎣⎢⎢⎢⎢⎢⎢⎢100⋮0010⋮0001⋮0⋯⋯⋯⋱⋯000⋮1⎤⎦⎥⎥⎥⎥⎥⎥⎥
满足 A∗I=I∗A=A ( A 为任意矩阵)
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
struct hp{
int a[35][35];
}A,unit;
int n,k,m;
inline hp jia(hp a,hp b){
hp c;
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
c.a[i][j]=(a.a[i][j]+b.a[i][j])%m;
return c;
}
inline hp cheng(hp a,hp b){
hp c;
memset(c.a,0,sizeof(c.a));
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
for (int k=1;k<=n;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%m)%m;
return c;
}
inline hp fast_pow(hp a,int p){
hp ans=unit;
for (;p;p>>=1,a=cheng(a,a))
if (p&1)
ans=cheng(ans,a);
return ans;
}
inline hp doit(int k){
if (k==1) return A;
hp now=doit(k/2);
if (k%2==0){
hp x=fast_pow(A,k/2);
x=jia(unit,x);
x=cheng(x,now);
return x;
}
else{
hp x=fast_pow(A,k/2+1);
x=jia(A,x);
x=cheng(x,now);
x=jia(A,x);
return x;
}
}
int main(){
scanf("%d%d%d",&n,&k,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
scanf("%d",&A.a[i][j]);
for (int i=1;i<=n;++i)
unit.a[i][i]=1;
hp ans=doit(k);
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
printf("%d%c",ans.a[i][j]," \n"[j==n]);
}