《算法竞赛进阶指南》0x3B Matrix Power Series

题目传送门

题目描述

给定 n × n n×n n×n 矩阵 A A A 和正整数 k k k,求和 S = A + A 2 + A 3 + … + A k S=A+A^2+A^3+…+A^k S=A+A2+A3++Ak

数据范围

1 ≤ n ≤ 30 , 1≤n≤30, 1n30,
1 ≤ k ≤ 1 0 9 , 1≤k≤10^9, 1k109,
1 ≤ m < 1 0 4 1≤m<10^4 1m<104

题解

本题中就没有什么隐含的递推式子了,直接裸着给出来了要求的值
这道题正解是用递归(分治)的方法来解决,但其实有一个巧妙而又神奇的方法,直接矩阵快速幂
我们构造出一个矩形:
[ A E 0 E ] 2 = [ A 2 A + E 0 E ] \left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^2= \left[ \begin{matrix} A^2 & A+E \\ 0 & E \end{matrix} \right] [A0EE]2=[A20A+EE]
E E E是指一个只有对角线( i = j i=j i=j)的点才为 1 1 1,其他的点都为 0 0 0的初始化矩阵,这样当一个矩阵与其做乘法时,得到的矩阵依然为它自身

观察这个简化的矩阵,我们对其进行幂运算
[ A E 0 E ] 2 = [ A 2 A + E 0 E ] \left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^2= \left[ \begin{matrix} A^2 & A+E \\ 0 & E \end{matrix} \right] [A0EE]2=[A20A+EE]
[ A E 0 E ] 3 = [ A 3 A 2 + A + E 0 E ] \left[ \begin{matrix} A & E \\ 0 & E \end{matrix} \right]^3= \left[ \begin{matrix} A^3 & A^2+A+E \\ 0 & E \end{matrix} \right] [A0EE]3=[A30A2+A+EE]
很容易可以观察出来,矩阵右上角的一项 − E -E E就是我们要求的最终的答案,也就是说,我们构造出这个矩阵,对它进行 ( m + 1 ) (m+1) (m+1)次方,得到的矩阵右上角的一部分 − E -E E即为所求

代码量应该是比递归写法要短不少的
注意:由于存在取模运算,所以得到的最后矩阵中可能有得点值恰好被模为0,当它又恰好在对角线上,要被减1时,就可能出现负数,所以要进行取模防负数

code
#include<bits/stdc++.h>
using namespace std;
int a[65][65],e[65][65];
int n,k,m;
void mul()
{
	int c[65][65];
	memset(c,0,sizeof(c));
	for(int i=1;i<=2*n;i++)
		for(int j=1;j<=2*n;j++)
			for(int k=1;k<=2*n;k++)
				c[i][j]=(c[i][j]+1ll*e[i][k]*a[k][j])%m;
	memcpy(e,c,sizeof(c));
}
void mulself()
{
	int c[65][65];
	memset(c,0,sizeof(c));
	for(int i=1;i<=2*n;i++)
		for(int j=1;j<=2*n;j++)
			for(int k=1;k<=2*n;k++)
				c[i][j]=(c[i][j]+1ll*a[i][k]*a[k][j])%m;
	memcpy(a,c,sizeof(c));
}


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[i][j]);
	for(int i=1;i<=n;i++) a[i][i+n]=a[i+n][i+n]=1;
	memcpy(e,a,sizeof(a));
	while(k)
	{
		if(k&1) mul();
		k>>=1;
		mulself();
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==j) printf("%d ",(e[i][j+n]-1+m)%m);
			else printf("%d ",e[i][j+n]);
		}
		puts("");	
	}	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值