[矩阵快速幂] AcWing 206 石头游戏

别告诉我你不知道什么是矩阵乘法 (╯▽╰)
题目传送门

看到题目,第一反应以为是模拟水题。结果一看数据范围 1 e 8 1e8 1e8
模拟看来是不行了,需要想复杂度更优的解法

考虑矩阵乘法
n × m n \times m n×m的矩阵映射到一个含有 n m nm nm 个元素的行向量
我们可以尝试构造新的状态转移矩阵 a a a,其行数和列数均为 n m n m nm,每次操作就相当于把一个 1 × n m 1 \times nm 1×nm的矩阵(每个格子里石子的数量)和状态转移矩阵相乘
a [ 0 ] [ 0 ] = 1 a[0][0]=1 a[0][0]=1
如果操作是 E E E,相当于把它右面的元素加上它的值,即 a[id(i,j)][id(i-1,j)]=1 i d ( i , j ) id(i,j) id(i,j)表示第 i i i j j j列元素在映射中的位置); W , S , E W,S,E W,S,E同理
如果操作是数字,就转移成 a[id(i,j)][id(i,j)]=1; a[0][id(i,j)]=x;(相当于保留原来的数字,同时加上新的数字 x x x
如果操作是 D D D,不用管,置空即可

此外,题目中有一个隐含条件,如果试图把石子放到不存在的格子里的话,把它当成 D D D 即可,所以要判断格子是否在边界处

操作序列长度不超过 6 6 6,所以状态转移矩阵一定会有循环节且循环节不超过 l c m ( 1 , 2 , 3 , 4 , 5 , 6 ) = 60 lcm(1,2,3,4,5,6)=60 lcm(1,2,3,4,5,6)=60
因此我们只需要计算出前 60 60 60个转移矩阵
求出前 60 60 60个矩阵的乘积,利用矩阵快速幂来求解乘积的 ⌊ t 60 ⌋ \lfloor \frac{t}{60} \rfloor 60t次方,剩下的 t % 60 t\%60 t%60个可以直接暴力乘上去,就可以愉快地求出最终 t t t秒时的状态转移矩阵啦

注意一点,数据范围会爆 i n t int int,注意开 l o n g   l o n g long~long long long


Code:

//ʯͷÓÎÏ·--¾ØÕó¿ìËÙÃÝ 
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,m,t,act,r[70];
char op[15][15];
struct Matrix{
	int n,m;
	ll a[70][70];
	Matrix operator*(const Matrix& rhs)const{	//矩阵乘法
		Matrix ans;
		memset(&ans,0,sizeof(ans));
		ans.n=n,ans.m=rhs.m;
		for(int i=0;i<=ans.n;i++){
			for(int j=0;j<=ans.m;j++){
				for(int k=0;k<=m;k++)
					ans.a[i][j]+=a[i][k]*rhs.a[k][j];
			}
		}
		return ans;
	}
	Matrix() {n=m=0;memset(a,0,sizeof(a));}
	void operator*=(const Matrix& rhs) {*this=*this*rhs;}
};
Matrix qpow(Matrix x,int y){	//矩阵快速幂
	Matrix ans;
	memset(&ans,0,sizeof(ans));
	ans.n=ans.m=x.n;
	for(int i=0;i<=n*m;i++) ans.a[i][i]=1;
	while(y){
		if(y&1) ans*=x;
		x*=x; y>>=1;
	}
	return ans;
}
int id(int x,int y) {return (x-1)*m+y;}	//建立映射
int mod(int x,int y){return x%y?x%y:y;}
signed main(){
	scanf("%d%d%d%d",&n,&m,&t,&act);
	{
		char tmp[15];
		for(int i=1;i<=n;i++){
			scanf("%s",tmp+1);
			for(int j=1;j<=m;j++) r[id(i,j)]=tmp[j]-'0'+1;	//每个格子对应的操作序列编号
		}
	}
	for(int i=1;i<=act;i++) scanf("%s",op[i]+1);
	
	/* computing */
	Matrix tt[65];	//1~60的状态转移矩阵
	memset(tt,0,sizeof(tt));
	for(int i=1;i<=60;i++){			//for 1 to 60 sec(matrix)
		tt[i].n=tt[i].m=n*m;
		tt[i].a[0][0]=1;
		for(int j=1;j<=n;j++){		//for line 1 to n
			for(int k=1;k<=m;k++){	//for col 1 to n
				char cop=op[r[id(j,k)]][mod(i,strlen(op[r[id(j,k)]]+1))];
				if(cop=='N'&&j>1) tt[i].a[id(j,k)][id(j-1,k)]=1;
				else if(cop=='W'&&k>1) tt[i].a[id(j,k)][id(j,k-1)]=1;
				else if(cop=='S'&&j<n) tt[i].a[id(j,k)][id(j+1,k)]=1;
				else if(cop=='E'&&k<m) tt[i].a[id(j,k)][id(j,k+1)]=1;
				else if(isdigit(cop)){	//指令是数字
					tt[i].a[id(j,k)][id(j,k)]=1;
					tt[i].a[0][id(j,k)]=cop-'0';
				}
			}
		}
	}
	
	Matrix eps;		//前60个矩阵的乘积
	eps.n=eps.m=n*m;
	for(int i=0;i<=eps.n;i++) eps.a[i][i]=1;
	for(int i=1;i<=60;i++) eps*=tt[i];

	Matrix final;	//前t个矩阵的乘积
	final.n=final.m=n*m;
	final=qpow(eps,t/60);
	for(int i=1;i<=t%60;i++) final*=tt[i];
	
	Matrix original;	//石子矩阵
	original.n=1,original.m=n*m;
	original.a[1][0]=1;
	original*=final;
	
	ll ans=0;
	for(int i=1;i<=n*m;i++) ans=max(ans,original.a[1][i]);
	printf("%lld\n",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值