[Agc003F]Fraction of Fractal
[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级分形中黑色单元格组成的连通分量数,模\(10^9+7\)。
Input
第一行三个整数H,W,K如题目描述接下来H行,每行W个字符
Output
输出K级分形中黑色单元格组成的连通分量数,模\(10^9+7\)。
Sample Input
3 3 3
.#.
.#
Sample Output
20
HINT
1≤H,W≤1000
0≤K≤1018
每个 si,j是 # 或 .
- 网格中所有黑色单元格四联通网格中
- 至少有一个黑色单元格
试题分析
第一眼:矩阵乘法。
怎么矩阵乘法?好像不会呀。。
然后没想就弃了,其实这题的突破点根据部分分想还是非常明显的。
考虑边界上没有黑块的情况:那么注定分形以后每个块都不连通,所以答案是\(x^{k-1}\)
其中\(x\)为黑块个数。
然后考虑分形之后的连通性:如果有两个黑块相连,那么分形之后肯定上一个图形的下面接在下一个图形的上面,那么如果这个图形之前的上下不连通,那么之后就不会再联通了,左右同理。
那么将上下左右都分别联通的拿出来单独考虑,发现无论多少次分形它们的答案都是1。
只剩下上下联通或者左右联通的情况了,重申一下联通是指自己下面和自己上面接在一起后有没有连在一起的黑块。
对于这些怎么处理呢?既然上下和左右有一边不连通了,那么肯定之后这边也不会联通,所以只需要考虑另一边(联通的一边)的联通块数量即可。
方便起见,这里设行联通(左右联通),首先显然分形\(k\)次之后点的个数是\(x^k\)个。
我们只需要用\(x^k\)减去左右联通并为一体的就可以了。
首先设初始图中\(y\)为左右都是黑色块的个数,\(z\)为左右联通行数。
如何统计呢?
分开考虑:
- 块内的:每个点都会拓展出\(y\)个黑色块对(\(x^{k-1}\times y\))。
- 块外的:也就是当前块之间多少个可以重合的,那么就是我们上面的\(z\)。
那么我们可以得出递推式:\(E_k=E_{k-1}\times z+x^{k-1}\times y\)
矩阵乘法即可,注意特判前两种情况,对于上下联通的只需要翻转一下矩阵就好了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
inline LL read(){
LL x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 100010;
const LL P = 1e9+7;
LL H,W,K; char str[1001][1001],str2[1001][1001];
inline int Type2(){
bool flag=0,flag2=0;
for(LL i=1;i<=H;i++){
if(str[i][W]=='#'&&str[i][1]=='#') {flag=1; break;}
} for(LL i=1;i<=W;i++){
if(str[1][i]=='#'&&str[H][i]=='#') {flag2=1; break;}
}
if(flag==1&&flag2==1) return 2;
if(flag|flag2) return 1;
else return 0;
}
inline LL Pow(LL a,LL b){
LL res=1LL; for(; b;b>>=1,a=a*a%P) if(b&1) res=res*a%P; return res;
}
struct Matrix{
LL A[11][11],x,y;
inline void init(LL xx,LL yy){
x=xx; y=yy; memset(A,0,sizeof(A));
}
inline void print(){
for(LL i=1;i<=x;i++){
for(LL j=1;j<=y;j++)
printf("%d ",A[i][j]);
printf("\n");
} return ;
}
}A,B;
Matrix operator * (Matrix a,Matrix b){
Matrix c; c.init(a.x,b.y);
for(LL i=1;i<=a.x;i++){
for(LL j=1;j<=b.y;j++)
for(LL k=1;k<=a.y;k++)
c.A[i][j]+=a.A[i][k]*b.A[k][j]%P,c.A[i][j]%=P;
} return c;
}
inline Matrix Pow(Matrix a,LL b){
Matrix c; c.init(2,2); for(LL i=1;i<=2;i++) c.A[i][i]=1;
while(b){ if(b&1) c=c*a; b>>=1; a=a*a; }
return c;
}
LL x,z,y;
inline void rev(){
for(LL i=1;i<=H;i++){
for(LL j=1;j<=W;j++)
str2[j][i]=str[i][j];
} swap(H,W); memcpy(str,str2,sizeof(str));
}
inline void Get(){
for(LL i=1;i<=H;i++){
if(str[i][1]=='#'&&str[i][W]=='#') ++z;
} if(!z) rev(); z=0; x=0; y=0;
for(LL i=1;i<=H;i++){
for(LL j=1;j<=W;j++){
if(str[i][j]=='#') ++x;
if(j<W&&str[i][j]=='#'&&str[i][j+1]=='#') ++y;
} if(str[i][1]=='#'&&str[i][W]=='#') ++z;
}
}
inline LL solve(){
Get();
A.init(2,2); A.A[1][1]=x; A.A[1][2]=y; A.A[2][2]=z;
A=Pow(A,K-1);
B.init(1,2); B.A[1][1]=1; B.A[1][2]=0;
B=B*A;
return ((B.A[1][1]-B.A[1][2])%P+P)%P;
}
int main(){
H=read(),W=read(); K=read();
if(H==1&&W==1){
puts("1"); return 0;
}
for(LL i=1;i<=H;i++){
scanf("%s",str[i]+1);
for(LL j=1;j<=W;j++)
if(str[i][j]=='#') ++x;
} int type=Type2();
if(!type) printf("%d\n",Pow(x,K-1));
else if(type==2) puts("1");
else printf("%lld\n",solve());
return 0;
}