题目的意思比较好理解,就是求等比矩阵的和,然后训练赛上,去怼矩阵快速幂,然后没怼过,超时,后来比赛结束,说是倍增法加矩阵快速幂去学了了一波,发现高中都学过,看来就是自己不会用,先说一下倍增法用来求等比数列求和,是一种速度比较快的方法,
下面介绍一下倍增法的原理的值。对于这个问题,用二分解决比较好。
(1)当时,
(2)当时,那么有
(3)当时,那么有
真的是学到了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct mat
{
int a[45][45];
mat()
{
memset(a,0,sizeof(a));
}
};
mat A;
mat I;
mat add(mat m1,mat m2)
{
mat ans;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
ans.a[i][j]=(m1.a[i][j]+m2.a[i][j])%10;
return ans;
}
mat mul(mat m1,mat m2)
{
mat ans;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
//if(m1.a[i][j])
for(int k=1; k<=n; k++)
ans.a[i][j]=(ans.a[i][j]+m1.a[i][k]*m2.a[k][j])%10;
return ans;
}
mat quickmul(mat m,int k)
{
mat ans=I;
while(k)
{
if(k&1) ans=mul(ans,m);
m=mul(m,m);
k>>=1;
}
return ans;
}
void print(mat m)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
printf("%d%c",m.a[i][j],j==n? '\n':' ');
}
mat getsum(int k)
{
if(k==1) return A;
mat ans=getsum(k/2);
if(k&1)
{
mat t=quickmul(A,k/2+1);
ans=add(mul(add(I,t),ans),t);
}
else
{
mat t=quickmul(A,k/2);
ans=mul(add(I,t),ans);
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m),n)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&A.a[i][j]),A.a[i][j]%=10;
for(int i=1; i<=n; i++) I.a[i][i]=1;
mat ans=getsum(m);
print(ans);
puts("");
}
return 0;
}
下面还有不是矩阵的做法,其实可以类推就知道,这只是关键的代码
LL power(LL a,LL b)
{
LL ans = 1;
a %= M;
while(b)
{
if(b & 1)
{
ans = ans * a % M;
b--;
}
b >>= 1;
a = a * a % M;
}
return ans;
}
LL sum(LL a,LL n)
{
if(n == 1) return a;
LL t = sum(a,n/2);
if(n & 1)
{
LL cur = power(a,n/2+1);
t = (t + t * cur % M) % M;
t = (t + cur) % M;
}
else
{
LL cur = power(a,n/2);
t = (t + t * cur % M) % M;
}
return t;
}