当前位置: 首页 > news >正文

刷题记录:牛客NC17890方格填色 [矩阵快速幂详解]

传送门:牛客

题目描述:

给一个m x n的方格,Applese想要给方格填上颜色,每个格子可以是黑色或者白色。他要求左右相邻两格不能
同为白色且相邻两列不能全为黑色。
输入:
3 5
输出:
1640

一道矩阵快速幂的好题

当时碰到这道题的时候是在状压dp的题单里碰到的,就…当时已经想出了应该使用递推的方式进行,递推的公式也不难推出,但是就是因为 n n n是在是太大了,导致递推数组肯定开不下的,当时也没想到矩阵乘法,所以,就卡了很久

但是当你想到这道题应该是用矩阵快速幂的时候,就意味着你离解出这道题不久了

那么今天就以这道题来好好的复习一下矩阵快速幂

首先是矩阵,矩阵是什么呢,就是一个形同二维数组的数据的集合,如下图就是一个 3 ∗ 3 3*3 33的矩阵

{ 1 2 3 4 5 6 7 8 9 } \left\{ \begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{matrix} \right\} 147258369

然后对于我们的矩阵乘法有以下的乘法准则:
两个大小分别为 m × n m \times n m×n n × p n \times p n×p 的矩阵 A , B A, B A,B 相乘的结果为一个大小为 m × p m \times p m×p 的矩阵。将结果矩阵记作 C C C,则
c i j = ∑ k = 1 n a i k b k j , ( 1 ≤ i ≤ m ,  1 ≤ j ≤ p ). c_{i j} = \sum_{k = 1}^{n} a_{i k} b_{k j} \text{,\qquad($1 \le i \le m$, $1 \le j \le p$).} cij=k=1naikbkj,(1im, 1jp).

而如果 A A A 的列数与 B B B 的行数不相等,则无法进行乘法。
可以验证,矩阵乘法满足结合律,即 ( A B ) C = A ( B C ) (A B) C = A (B C) (AB)C=A(BC)
一个大小为 n × n n \times n n×n 的矩阵 A A A 可以与自身进行乘法,得到的仍是大小为 n × n n \times n n×n 的矩阵,记作 A 2 = A × A A^2 = A \times A A2=A×A。进一步地,还可以递归地定义任意高次方 A k = A × A k − 1 A^k = A \times A^{k - 1} Ak=A×Ak1,或称 A k = A × A × ⋯ × A ⏟ k  次 A^k = \underbrace{A \times A \times \cdots \times A}_{k \text{ 次}} Ak=k  A×A××A

特殊地,定义 A 0 A^0 A0 为单位矩阵 I = [ 1 0 ⋯ 0 0 1 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ 1 ] I = \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix} I= 100010001

那么对于矩阵乘法来说,满足我们的快速幂性质,因为对于矩阵来说满足乘法结合律和加法结合律,也就是对于矩阵来说,我们是可以对此采用类快速幂计算的.

所以对于快速幂中的乘法,我们只要将两个矩阵按照上述的矩阵乘法法则进行即可

void juzhen_qpow(int k) {
	while(k) {
		if(k&1) ans_pow();
		x_pow();
		k>>=1;
	}
}

至此我们的矩阵快速幂部分结束


接下来是这道题的讲解.

对于本题来说,我们可以将黑色部分记为1,白色部分记为0,那么对于每一列来说我们就可以使用二进制来记录该列的状态

那么对于本题的限制来说就是相邻两类的状态 S 1 S_1 S1, S 2 S_2 S2的,然后 S 1 S_1 S1 ∣ \mid S 2 S_2 S2的值为 ( 1 < < m ) − 1 (1<<m)-1 (1<<m)1并且 S 1 S_1 S1, S 2 S_2 S2不能都为 ( 1 < < m ) − 1 (1<<m)-1 (1<<m)1.

然后呢,我们可以将 S 1 , S 2 S_1,S_2 S1,S2的状态都记为矩阵的形式

{ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] . . . } \left\{ \begin{matrix} f[0][m]\\f[1][m] \\ f[2][m] \\ f[3][m] \\...\end{matrix} \right\} f[0][m]f[1][m]f[2][m]f[3][m]...

然后我们找一下前后矩阵的关系,就会发现 S 1 S_1 S1矩阵显然可以经过一个01矩阵相乘得到 S 2 S_2 S2,因为对于每一个状态来说,前一种状态都可以或不可以转移到后一种状态,那么根据矩阵来说就是01矩阵的相乘.

举个栗子,当 n = 2 n=2 n=2时我们有以下的递推公式

{ f [ 0 ] [ m ] f [ 1 ] [ m ] f [ 2 ] [ m ] f [ 3 ] [ m ] } \left\{ \begin{matrix} f[0][m]\\f[1][m] \\ f[2][m] \\ f[3][m] \\\end{matrix} \right\} f[0][m]f[1][m]f[2][m]f[3][m] = = = { 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 } \left\{ \begin{matrix} 0&0&0&1\\0&0&1&1\\ 0&1&0&1 \\ 1&1&1&0\\\end{matrix} \right\} 0001001101011110 ∗ \ast { f [ 0 ] [ m − 1 ] f [ 1 ] [ m − 1 ] f [ 2 ] [ m − 1 ] f [ 3 ] [ m − 1 ] } \left\{ \begin{matrix} f[0][m-1]\\f[1][m-1] \\ f[2][m-1] \\ f[3][m-1] \\\end{matrix} \right\} f[0][m1]f[1][m1]f[2][m1]f[3][m1]

并且对于我们的第一列来说我们的所有的值都是1
{ f [ 0 ] [ 0 ] f [ 1 ] [ 0 ] f [ 2 ] [ 0 ] f [ 3 ] [ 0 ] } \left\{ \begin{matrix} f[0][0]\\f[1][0] \\ f[2][0] \\ f[3][0] \\\end{matrix} \right\} f[0][0]f[1][0]f[2][0]f[3][0] = = = { 1 1 1 1 } \left\{ \begin{matrix} 1\\1 \\1 \\1\\\end{matrix} \right\} 1111

那么对于第 n n n列来说,我们最终的答案就是上述矩阵矩阵系数的 N N N次方即可,就是一个等比数列,那么直接使用之前讲过的矩阵快速幂解决即可.

注意因为对于矩阵来说我们需要使用矩阵相乘才行,所以我们本来在快速幂中的 a n s = 1 ans=1 ans=1,此时要换算成矩阵的形式,也就是之前讲过的单位矩阵就行

至此我们即解决了这道题

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define int long long
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
const int mod=1e9+7;
int m,n;int a[1<<6][1<<6];int ans[1<<6][1<<6];int c[1<<6][1<<6];
void ans_pow() {
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			c[i][j]=ans[i][j];
			ans[i][j]=0;
		}
	}
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			for(int k=0;k<1<<m;k++) {
				ans[i][j]=(ans[i][j]+c[i][k]*a[k][j])%mod;
			}
		}
	}
}
void x_pow() {
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			c[i][j]=a[i][j];
			a[i][j]=0;
		}
	}
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			for(int k=0;k<1<<m;k++) {
				a[i][j]=(a[i][j]+c[i][k]*c[k][j])%mod;
			}
		}
	}
}
void juzhen_qpow(int k) {
	while(k) {
		if(k&1) ans_pow();
		x_pow();
		k>>=1;
	}
}
signed main() {
	m=read(),n=read();
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			if((i|j)==(1<<m)-1&&!(i==(1<<m)-1&&j==(1<<m)-1)) {
				a[i][j]=1;
			}else {
				a[i][j]=0;
			}
		}
	}
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			if(i==j) {
				ans[i][j]=1;
			}else {
				ans[i][j]=0;
			}
		}
	}
	juzhen_qpow(n-1);
	int Ans=0;
	for(int i=0;i<1<<m;i++) {
		for(int j=0;j<1<<m;j++) {
			Ans=(Ans+ans[i][j])%mod;
		}
	}
	cout<<Ans<<endl;
	return 0;
}

相关文章:

  • 产地证是在哪个网站上做/网站关键词排名优化价格
  • 网站做过备案后能改别的公司吗/网站推广和优化系统
  • 淘宝优惠的网站怎么做/超级外链吧
  • 网站建设初步规划/下载百度app最新版并安装
  • 中国电信网站备案流程/上海seo公司哪个靠谱
  • 如何建设网络营销网站/怎么在网上做广告宣传
  • CSS -- CSS使用过渡(transition)添加动画
  • USB TO SPI(上海同旺电子)调试器调试MCP4822
  • RK3568下载SDK编译源码
  • mock功能
  • 使用 kube-prometheus(release-0.6) 监控 Kubernetes v1.18.20
  • Numpy+PIL实现图片的自由旋转
  • 向外搜索(OS)算法是一种新算法,旨在为改进进化算法的收敛性提供多种形式(Matlab代码实现)
  • 54.Python的def语句自定义函数
  • 力扣(LeetCode)187. 重复的DNA序列(C++)
  • 喜讯丨计讯物联荣获厦门软件园党群服务中心篮球赛亚军
  • 5G MEC UPF选择及本地分流技术分析
  • 电磁场的变化方式 工程电磁场 P27