很神的题!!!
首先观察一下每次转移
如果初始的图案存在某一行 i ,满足行的两个端点为黑,且存在某一列也是满足这样的条件,那么答案就是1,因为原来就连通的黑块转移后的图案也是联通的。
如果行和列都不满足,因为每次转移答案都会乘
如果只有行满足这样的条件(列满足的话转一下图案),
设
那么每次转移就有
x′=x2 - y′=xy+yz
- z′=z2
这个东西退一下可以知道…
用矩阵转移就好了…
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <assert.h> using namespace std; typedef long long ll; const int N=1010,P=1e9+7; struct mat{ int a[2][2]; mat(){ a[0][0]=a[1][1]=a[0][1]=a[1][0]=0; } int *operator [](int x){ return a[x]; } friend mat operator *(mat a,mat b){ mat c; c[0][0]=(1LL*a[0][0]*b[0][0]+1LL*a[0][1]*b[1][0])%P; c[0][1]=(1LL*a[0][0]*b[0][1]+1LL*a[0][1]*b[1][1])%P; c[1][0]=(1LL*a[1][0]*b[0][0]+1LL*a[1][1]*b[1][0])%P; c[1][1]=(1LL*a[1][0]*b[0][1]+1LL*a[1][1]*b[1][1])%P; return c; } }; mat u; int n,m; char a[N][N],b[N][N]; ll k; inline int judge(){ for(int i=1;i<=n;i++) if(a[i][1]=='#' && a[i][m]=='#') return 1; return 0; } inline void turn(){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[m-j+1][i]=a[i][j]; swap(n,m); memcpy(a,b,sizeof(a)); } inline int Pow(int x,ll y){ int ret=1; for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P; return ret; } inline mat Pow(mat x,ll y){ mat ret=u; for(;y;y>>=1,x=x*x) if(y&1) ret=ret*x; return ret; } int main(){ freopen("1.in","r",stdin); freopen("1.out","w",stdout); u[0][0]=u[1][1]=1; scanf("%d%d%lld",&n,&m,&k); int cnt=0; for(int i=1;i<=n;i++){ scanf("%s",a[i]+1); for(int j=1;j<=m;j++) cnt+=a[i][j]=='#'; } int A=judge(),B=(turn(),judge()); turn(); if((A && B) || !k) return puts("1"),0; if(!A && !B) return printf("%d\n",Pow(cnt,k-1)),0; if(!A) turn(); int c=0,b=0; for(int i=1;i<=n;i++) c+=(a[i][1]=='#' && a[i][m]=='#'); for(int i=1;i<=n;i++) for(int j=1;j<m;j++) b+=(a[i][j]=='#' && a[i][j+1]=='#'); mat w,ans; w[0][0]=cnt; w[0][1]=b; w[1][1]=c; ans=Pow(w,k-1); printf("%d\n",(ans[0][0]+P-ans[0][1])%P); return 0; }