【矩阵乘法】【AGC003】F - Fraction of Fractal

F - Fraction of Fractal

题意:

给出一个大小为 n m n*m 的只有黑色或白色格子的图,这个图被我们称作1级构型。1级构型保证所有的黑色格子是联通(从上下或左右走可达)的。当初就因为没有注意到这句话将一个错误的思路想了半天。读题要仔细啊!!尤其是英文
特别的,0级构型是一个 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} ;
接下来讨论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
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 1 ] × f 1 E[k-1]×f1 表示在‘块’与‘块’之间连的边, V [ k 1 ] × s 1 V[k-1]×s1 表示在一个‘块’的内部连的边
由于k达到了 1 0 18 10^{18} ,所以用矩阵乘法优化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);
}

猜你喜欢

转载自blog.csdn.net/qq_41343943/article/details/82765203
今日推荐