传送门
题意
有一个n*m的矩阵,矩阵上每一个格子有四个传送门,分别通向四个格子,题目给出了每个格子的四个传送门所能到达的地方。起点在(1,1),终点是(n,m),当走到终点的时候就不能再走了,也就是说一旦你到达了终点,就会直接离开这个矩阵。问说从起点开始走P(0 ≤ P ≤ 100,000,000)步能不能到达终点。
输出的情况是True,Maybe和False。Ture对应的就是走了P步以后只能到达一个点,就是终点。Maybe对应的是走了P步之后还可以到达除了终点以外的其他点。False对应的是P步以后无法到达终点。
分析
(显然的矩阵乘法?),不太能理解(菜),看了这篇blog,照着思路敲了敲,2a
图的临接矩阵A的 p次方Ap中为1的元素(i,j)表示节点i到节点j有一条长度为p的路径(经历的节点可能重复)。要理解矩阵的含义,两个矩阵相乘如果(x,y)元素为1,而(y,z)元素为1,则结果(x,z)元素为1,这表明通过y把x和z连起来了。而题目要求经过终点就不能走了,所以在做矩阵乘法时,需要把(x,n-1) (n-1,y)这样决定的(x,y)去掉。(n-1表示终点)。做乘法时,中间点小心一点就好了。矩阵乘法和floyd在本质上是一样的……
矩阵的P次方运用的是经典的log(P)的算法。最后看一下结果矩阵的首行(1行)里面有几个1,以及(1,n*m)是不是1,来决定结果。
trick
1.开longlong
2.读入加两个getchar()
3.不处理(n,m)的传送门
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
int t,n,m,x1,y1,x2,y2,x3,y3,x4,y4,q,p;
struct matrix
{
ll a[50][50];
}ans,ret;
matrix multi(matrix x,matrix y)
{
int num=n*m;
matrix tmp;
for(int i=1;i<=num;++i)for(int j=1;j<=num;++j)
{
tmp.a[i][j]=0;
for(int k=1;k<=num;++k) tmp.a[i][j]+=x.a[i][k]*y.a[k][j];
}
return tmp;
}
void quick_mod(int p)
{
matrix r=ret;
int num=n*m;
for(int i=1;i<=num;++i)for(int j=1;j<=num;++j) ans.a[i][j]=(i==j)?1:0;
for(;p;p>>=1,r=multi(r,r)) if(p&1) ans=multi(ans,r);
}
int main()
{
for(scanf("%d",&t);t--;)
{
scanf("%d %d",&n,&m);getchar();
memset(ret.a,0,sizeof(ret.a));
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
{
scanf("((%d,%d),(%d,%d),(%d,%d),(%d,%d))",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
getchar();
if(i==n&&j==m) continue;
ret.a[(i-1)*m+j][(x1-1)*m+y1]=1;
ret.a[(i-1)*m+j][(x2-1)*m+y2]=1;
ret.a[(i-1)*m+j][(x3-1)*m+y3]=1;
ret.a[(i-1)*m+j][(x4-1)*m+y4]=1;
}
//for(int i=1;i<=n*m;++i)for(int j=1;j<=n*m;++j) printf("%d%c",ret.a[i][j],(j==n*m)?'\n':' ');
scanf("%d",&q);
while(q--)
{
scanf("%d",&p);
quick_mod(p);
if(ans.a[1][n*m]==0) { puts("False");continue; }
bool flag=0;
for(int i=1;i<n*m;++i) if(ans.a[1][i]) { flag=1;break; }
if(!flag) puts("True");else puts("Maybe");
}
puts("");
}
}