题意:石头游戏在一个
序列中的每个字符是以下格式之一:
- 数字
:表示拿 个石头到该格子。 :表示把这个格子内所有的石头推到相邻的格子, 表示上方, 表示左方, 表示下方, 表示右方。 :表示拿走这个格子的所有石头。
给定每种操作序列对应的字符串,以及网格中每个格子对应的操作序列,求石头游戏进行了
解析:一道矩阵乘法的BZOJ权限题。
设
为了让题目更好做,我们把网格看成一个长度为
特别地,我们要使
这里我们把
因为操作序列长度不超过
对与
- 如果网格
第 秒的操作字符为 ,且 ,就令 ,表示把石头推到上面去,其他方向的操作类似,注意判断边界。 - 如果网格
第 秒的操作字符为一个数字 ,就令 , 。表示格子从 拿 颗石头,并且保持原有石头不变。 ,使 在转移过程中保持为 。 - 将
其余赋值为 。
这四点的正确性证明,手动模拟一下就知道,第一个操作
最后使用矩阵乘法,使
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
long long n,m,t,act,maxx=-1e9;
char a[25][25],b[25][25],len[25];
struct matrix
{
long long map[71][71];
}x,ans,s[61],now;
matrix multi(matrix a,matrix b)
{
matrix c=now;
for(int i=0;i<=n*m;i++)
for(int j=0;j<=n*m;j++)
for(int k=0;k<=n*m;k++)
c.map[i][j]+=a.map[i][k]*b.map[k][j];
return c;
}
void power(long long b)
{
for(;b;b>>=1)
{
if(b&1)
ans=multi(ans,x);
x=multi(x,x);
}
}
long long num(int i,int j)
{
return (i-1)*m+j;
}
int main()
{
scanf("%lld%lld%lld%lld",&n,&m,&t,&act);
for(int i=1;i<=n;i++)
scanf("%s",a[i]);
for(int i=0;i<act;i++)
{
scanf("%s",b[i]);
len[i]=strlen(b[i]);
}
for(int i=0;i<=n*m;i++)
x.map[i][i]=1;
ans.map[0][0]=1;
for(int step=0;step<60;step++)
{
s[step].map[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int q=a[i][j-1]-'0';
char r=b[q][step%len[q]];
if(r>='0'&&r<='9')
{
s[step].map[0][num(i,j)]=r-'0';
s[step].map[num(i,j)][num(i,j)]=1;
}
if(r=='N'&&i>1)
s[step].map[num(i,j)][num(i-1,j)]=1;
if(r=='S'&&i<n)
s[step].map[num(i,j)][num(i+1,j)]=1;
if(r=='E'&&j<m)
s[step].map[num(i,j)][num(i,j+1)]=1;
if(r=='W'&&j>1)
s[step].map[num(i,j)][num(i,j-1)]=1;
}
x=multi(x,s[step]);
}
power(t/60);
for(int i=0;i<t%60;i++)
ans=multi(ans,s[i]);
for(int i=1;i<=n*m;i++)
maxx=max(maxx,ans.map[0][i]);
printf("%lld",maxx);
return 0;
}