[AGC003F] Fraction of Fractal(矩阵乘法)
Description
Snuke从他的母亲那里得到了生日礼物——一个网格。网格有H行W列。每个单元格都是黑色或白色。所有黑色单元格都是四联通的,也就是说,只做水平或垂直移动且只经过黑色单元格即可从任何黑色单元格移动到任何其他黑色单元格。
第i行第j列的单元格的颜色由字符si,j表示。如果si,j是 #,该单元格为黑色;如果si,j是 .,该单元格为白色。至少一个单元格是黑色的。
我们定义「分形」如下:0级分形是一个 1×1的黑色单元格.k级分形由H行W列较小一级的分形按照 Snuke 的网格的样式拼成:与Snuke 网格中的黑色单元格对应的位置是一个k级分形;与Snuke 网格中的白色单元格对应的位置是一个单元格全部为白色,尺寸与k级分形相同的网格。
您将得到 Snuke 的网格的描述和整数 K。请求出K级分形中黑色单元格组成的连通分量数,模1000000007。
Input
第一行三个整数H,W,K如题目描述
接下来H行,每行W个字符
Output
输出K级分形中黑色单元格组成的连通分量数,模1000000007。
题解:
一看 \(K=10^8\) 肯定是log级别的算法,一下想到矩阵快速幂。
首先,一个网格的上面和下面,左边和右边都连通,那么不管怎么分它都是连通的,所以答案是 \(1\)。
如果一个网格上面和下面,左边和右边都不连通,那么不管怎么分都是不连通的,所以答案是 \(x^{k-1}\),\(x\) 为黑块总个数。
剩下就是行连通,列不连通了。(不然转一下就好了)
x[k] 表示 k 级分型有多少个黑块。
y[k] 表示 k 级分型有多少个黑块满足这行的下一个也是黑块。
z[k] 表示 k 级分型有多少行是连通的。
手推可得:
\(x[k]=x[k-1]^2\)
\(y[k]=x[k-1]y[k-1]+z[k-1]y[k-1]\)
\(z[k]=z[k-1]^2\)
构造矩阵:
求这个矩阵的 \(k-1\) 次方,答案为 \(x-y\)。
CODE:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mod 1000000007
int n,m;
char s[1005][1005],tmp[1005][1005];
long long k;
struct Matrix{
long long mat[5][5];
Matrix(){memset(mat,0,sizeof(mat));}
Matrix operator*(const Matrix &b)const{
Matrix c;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++){
for(int k=1;k<=2;k++)
c.mat[i][j]+=mat[i][k]*b.mat[k][j];
c.mat[i][j]%=mod;
}
return c;
}
Matrix operator^(long long x)const{
Matrix a(*this),ans;
for(int i=1;i<=2;i++)ans.mat[i][i]=1;
while(x){
if(x&1)ans=ans*a;
x>>=1,a=a*a;
}
return ans;
}
}mat;
int qpow(int x,long long y){
int ans=1;
while(y){
if(y&1)ans=1LL*ans*x%mod;
y>>=1,x=1LL*x*x%mod;
}
return ans;
}
bool check(){
for(int i=1;i<=n;i++)
if(s[i][1]=='#'&&s[i][m]=='#')return true;
return false;
}
void turn(){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
tmp[m-j+1][i]=s[i][j];
swap(n,m);
memcpy(s,tmp,sizeof(s));
}
int main(){
scanf("%d%d%lld",&n,&m,&k);
int x=0,y=0,z=0;
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)x+=s[i][j]=='#';
}
bool a=check(),b=(turn(),check());turn();
if(a&&b)return printf("1"),0;
else if(!a&&!b)return printf("%d",qpow(x,k-1)),0;
else if(b)turn();
for(int i=1;i<=n;i++)
z+=s[i][1]=='#'&&s[i][m]=='#';
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++)y+=s[i][j]=='#'&&s[i][j+1]=='#';
mat.mat[1][1]=x,mat.mat[1][2]=y,mat.mat[2][2]=z;
mat=mat^(k-1);
printf("%lld",(mat.mat[1][1]-mat.mat[1][2]+mod)%mod);
}