Atcoder Grand Contest 003 F Fraction of Fractal(矩阵乘法)

Atcoder 题面传送门 & 洛谷题面传送门

Yet another AGC F,然鹅这次就没能自己想出来了……

首先需注意到题目中有一个条件叫做“黑格子组成的连通块是四联通的”,这意味着我们将所有黑格都替换为题目中 \(H\times W\) 的标准型之后,黑格(标准型)内部是不会对连通块个数产生贡献的,产生贡献的只可能是黑格与黑格之间的边不产生连通块。如果我们记 \(G_{\text{h}}\) 两个标准型横着拼在一起得到的 \(H\times 2W\) 的图形,\(G_{\text{v}}\) 为两个标准型竖着拼在一起得到的 \(2H\times W\) 的图形,那么稍微分析一下即可得到以下性质:

  • 如果 \(G_{\text{h}},G_{\text{v}}\) 中都只有一个连通块,那么最终得到的 \(k\) 级分形也只有一个连通块。因为对于原来的标准型中的相邻两个黑格,由于 \(G_{\text{h}},G_{\text{v}}\) 连通,所以将这两个黑格分别替换为标准型后,这两个黑格所表示的连通块依旧连通,又因为这个标准型本来就是连通图,所以进行一次迭代(将黑格替换为标准型)后,该图中所有黑格表示的标准型都是连通的,也就是说该标准型的二级分形为连通图。又 \(\because G_{\text{h}}\) 连通,仿照上述推理过程可知 \(G_{\text{h}}\) 进行一次迭代后之后,所得的分形仍是连通图,即将该标准型的二级分形左右拼接后,可以得到连通形,\(G_{\text{v}}\) 也同理,如此归纳下去即可证明。
  • 如果 \(G_{\text{h}},G_{\text{v}}\) 中都有两个连通块,那么最终得到的 \(k\) 级分形中有 \(\text{标准型中黑格个数}^{k-1}\) 个黑格,这个好办,\(G_{\text{h}},G_{\text{v}}\) 都有两个连通块,这意味着对于标准型中的任意两个相连的黑格,将它们分别替换为标准型后,彼此之间互不连通,也就是每个黑格各自成一个连通块,记原来的标准型中黑格个数为 \(C\),那么根据之前的推论,二级分形中有 \(C\) 个连通块,\(C^2\) 个黑格,三级分形中有 \(C^2\) 个连通块,\(C^3\) 个黑格,以此类推。

比较棘手的是 \(G_{\text{h}},G_{\text{v}}\) 刚好一个连通,一个不连通的情况。不妨设 \(G_{\text{h}}\) 连通,\(G_{\text{v}}\) 不连通。我们考虑探究一次迭代会对连通块个数产生什么影响。我们考虑使用一个并查集的思想,不妨假设所有黑格替换为的分形都单独成一个连通块,然后将能并的并起来,显然两个黑格表示的连通块能够并起来当且仅当它们左右相邻,而显然左右相邻的格子不会组成环,也就是说每次合并都是合法的,这样减少的连通块个数就是左右相邻的黑格个数,故一次迭代之后连通块的个数,就等于黑格个数减去左右相邻的黑格个数。

考虑怎样计算这个东西,设 \(a_k\)\(k\) 级分形中左右相邻的黑格个数,\(b_k\)\(k\) 级分形中的黑格个数,那么显然可以推出 \(b\) 的递推式 \(b_{k+1}=b_kb_1\),但是推出 \(a\) 的递推式还有些困难。考虑将 \(k\) 级分形替换为 \(k+1\) 级分形后,左右相邻的黑格的来头,显然它可能是一个黑格替换为一个标准型分形后,内部出现左右相邻的黑格,这部分我们是可以计算的,为 \(b_ka_1\),也有可能是本身左右相邻的黑格替换为标准型后,边界上出现左右相邻的黑格,我们发现这东西无法直接表示,那就设一下呗。再设 \(c_k\)\(k\) 级分形中左右拼接的边界上左右相邻的黑格个数,设完之后这东西就很好表示了,为 \(a_kc_1\),即 \(a_{k+1}=b_ka_1+a_kc_1\)。最后考虑 \(c\) 的递推式,显然边界上相邻只可能是原来边界上相邻的黑格被替换为标准型后,每对相邻的黑格新多出 \(c_1\) 对出来,即 \(c_{k+1}=c_kc_1\)

最后看下数据范围……\(10^{18}\),还有这么多递推式,那显然矩阵快速幂咯。稍微推推即可得到 \(\begin{bmatrix}b_k&a_k\\0&c_k\end{bmatrix}\times\begin{bmatrix}b_1&a_1\\0&c_1\end{bmatrix}=\begin{bmatrix}b_{k+1}&a_{k+1}\\0&c_{k+1}\end{bmatrix}\),即 \(\begin{bmatrix}b_k&a_k\\0&c_k\end{bmatrix}=\begin{bmatrix}b_1&a_1\\0&c_1\end{bmatrix}^k\),快速幂即可。

const int MAXN=1e3;
const int MOD=1e9+7;
int n,m;ll k;
char s[MAXN+5][MAXN+5];
int qpow(int x,ll e){
	int ret=1;
	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
	return ret;
}
struct mat{
	ll a[2][2];
	mat(){memset(a,0,sizeof(a));}
	mat operator *(const mat &rhs){
		mat 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]+=a[i][k]*rhs.a[k][j];
		for(int i=0;i<2;i++) for(int j=0;j<2;j++) ret.a[i][j]%=MOD;
		return ret;
	}
};
int main(){
	scanf("%d%d%lld",&n,&m,&k);if(!k) return puts("1"),0;
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	int ud_bor=0,lr_bor=0,ud_ins=0,lr_ins=0,cnt=0;
	for(int i=1;i<=m;i++) ud_bor+=(s[1][i]=='#'&&s[n][i]=='#');
	for(int i=1;i<=n;i++) lr_bor+=(s[i][1]=='#'&&s[i][m]=='#');
	for(int i=1;i<n;i++) for(int j=1;j<=m;j++) ud_ins+=(s[i][j]=='#'&&s[i+1][j]=='#');
	for(int i=1;i<=n;i++) for(int j=1;j<m;j++) lr_ins+=(s[i][j]=='#'&&s[i][j+1]=='#');
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cnt+=(s[i][j]=='#');
//	printf("%d %d %d %d %d\n",ud_bor,lr_bor,ud_ins,lr_ins,cnt);
	if(!ud_bor&&!lr_bor) printf("%d\n",qpow(cnt,k-1));
	else if(ud_bor&&lr_bor) printf("1\n");
	else{
		mat trs,res;res.a[0][0]=res.a[1][1]=1;--k;
		if(ud_bor) trs.a[0][0]=cnt,trs.a[0][1]=ud_ins,trs.a[1][1]=ud_bor;
		else trs.a[0][0]=cnt,trs.a[0][1]=lr_ins,trs.a[1][1]=lr_bor;
		for(;k;k>>=1,trs=trs*trs) if(k&1) res=res*trs;
		printf("%d\n",(res.a[0][0]-res.a[0][1]+MOD)%MOD);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/agc003F.html