F - Fraction of Fractal
题意:
给出一个大小为
n
∗
m
n*m
n∗m的只有黑色或白色格子的图,这个图被我们称作1级构型。1级构型保证所有的黑色格子是联通(从上下或左右走可达)的。当初就因为没有注意到这句话将一个错误的思路想了半天。读题要仔细啊!!尤其是英文
特别的,0级构型是一个
1
∗
1
1*1
1∗1的黑色格子。
定义一个k级构型:它由k-1级构型转化而来,怎么转化呢?首先,它具有与1级构型的格子数量和形状都相同的‘块’,每一个‘块’都与1级构型中的格子一一对应。而这个’块’的大小和形状与k-1级构型相同。如果这个‘块’对应的1级构型中的格子是白色,那么这个‘块’中所有格子都为白色,否则,这个‘块’就是k-1级构型。
给出n,m,k,和1级构型,求k级构型中联通块的数量。
思路:
记cnt为1级构型中黑格子的数量,s1为上下相邻的黑格子数,s2为左右相邻的黑格子数,f1为一列中,第一行与最后一行都是黑格子的数目,f2为一行中,第一列和最后一列都是黑格子的数目。
画一画图,可以很明显地看出来:
若f1>0且f2>0,无论多少级构型,所有黑色格子始终联通,答案为1;
若f1=0且f2=0,ans[k]=ans[k-1]*cnt,答案为cnt
k
−
1
^{k-1}
k−1;
接下来讨论f1和f2只有1个为0的情况:不妨设f1>0,f2=0,(若f2>0,可以翻转图形,就等价于这种情况)。
联通块数量C=V-E,
那么考虑计算 k 级构型对应的图中的点数 V 和边数 E ,有递推式:
V
[
k
]
=
V
[
k
−
1
]
×
c
n
t
,
V
[
0
]
=
1
V[k]=V[k-1]×cnt, V[0]=1
V[k]=V[k−1]×cnt,V[0]=1
E
[
k
]
=
E
[
k
−
1
]
×
f
1
+
V
[
k
−
1
]
×
s
1
,
E
[
0
]
=
0
E[k]=E[k-1]×f1+V[k-1]×s1,E[0]=0
E[k]=E[k−1]×f1+V[k−1]×s1,E[0]=0
其中,
E
[
k
−
1
]
×
f
1
E[k-1]×f1
E[k−1]×f1表示在‘块’与‘块’之间连的边,
V
[
k
−
1
]
×
s
1
V[k-1]×s1
V[k−1]×s1表示在一个‘块’的内部连的边
由于k达到了
1
0
18
10^{18}
1018,所以用矩阵乘法优化dp.
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 1010
#define MO 1000000007
#define LL long long
int cnt,s1,s2,f1,f2,n,m;
LL k;
char s[MAXN][MAXN];
LL PowMod(LL a,LL b)
{
b%=(MO-1);
LL ret=1;
while(b)
{
if(b&1) ret=ret*a%MO;
a=a*a%MO;
b>>=1;
}
return ret;
}
struct maz
{
LL a[2][2];
maz(){memset(a,0,sizeof a);};
maz operator*(maz &m)
{
maz ret;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
ret.a[i][j]=(ret.a[i][j]+a[i][k]*m.a[k][j]%MO)%MO;
return ret;
}
};
int main()
{
scanf("%d%d%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)
if(s[i][j]=='#')
{
cnt++;
s1+=(s[i-1][j]=='#');
s2+=(s[i][j-1]=='#');
}
if(s[i][1]=='#'&&s[i][m]=='#') f2++;
if(i==n)
{
for(int j=1;j<=m;j++)
if(s[1][j]=='#'&&s[n][j]=='#') f1++;
}
}
if((f1&&f2)||k==0)
{
printf("1\n");
return 0;
}
else if(f1==0&&f2==0)
{
printf("%lld\n",PowMod(cnt,k-1));
return 0;
}
if(f1==0)
{
swap(f1,f2);
swap(s1,s2);
}
maz A,ans;
A.a[0][0]=cnt,A.a[1][0]=s1,A.a[1][1]=f1;
ans.a[0][0]=ans.a[1][1]=1;
k--;
while(k)
{
if(k&1) ans=ans*A;
A=A*A;
k>>=1;
}
printf("%lld\n",(ans.a[0][0]-ans.a[1][0]+MO)%MO);
}