【矩阵快速幂】NC17890-方格填色

题目链接

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 32768K,其他语言65536K

64bit IO Format: %lld

题目描述

给一个\(m * n\)的方格,Applese想要给方格填上颜色,每个格子可以是黑色或者白色。他要求左右相邻两格不能同为白色且相邻两列不能全为黑色。

求满足条件的方案数。

输入描述:

输入两个整数m, n。(1 ≤ m ≤ 5, 1 ≤ n ≤ 1e18)。

输出描述:

输出答案对1e9 + 7取模的结果。

示例1

输入

3 1

输出

8

示例2

输入

3 5

输出

1640

示例3

输入

5 5

输出

351032

思路

题意如题不用解读

因为\(m\)很小,我们可以考虑一列一列的去看,每个格子是黑色或者白色,那么每一列最多能有\(2^m\)个方案数,我们可以用一个动态规划来求方案数

\(st\)是一个\(0\)\(2^m - 1\)的一个数,看成是\(m\)位的二进制,表示涂色状态,0是白色,1是黑色

\(n\)特别大,这个状态是可以递推的,但不能用线性,这个时候可以使用一种常用的优化线性递推的数论知识,矩阵快速幂

那么可以设\(f(n,st) = \sum f(n-1,st')\),来构造一个转移矩阵,题目需要满足一些条件,没有相邻的两列中两个相邻的格为白色并且没有相邻两列全黑。

容易知道可以用限制条件\(st|st' = 2 ^m-1\)来得到第一个条件的可行方案,并且\(st,st'\)不能同为\(2^m-1\)来得到第二个条件的可行方案,设转移矩阵为\(trans\),那么\(trans.a[st][st']\)在两格条件都满足时为1,否则为0,一直往下递推可以知道:

最终答案为\(\sum_{st=0}^{2^m-1}f(n,st)\)

矩阵快速幂复杂度\(O((2^m)^3*logn)\)

(就写出了两个板子题把自己高兴坏了,不知道在高兴什么)

AC代码

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
#define endl '\n'
const int N = 40;
const int mod = 1e9 + 7;
const double pi = acos(-1.0);
typedef long long ll;
int t, n, m, M;
struct Matrix {//矩阵初始化,乘法重载
	int a[N][N];//2^5只有32,开40*40足够
	Matrix()
	{
		for (int i = 0; i < m; i++) {
			for (int j = 0; j < m; j++) {
				a[i][j] = 0;
			}
		}
	}
	Matrix operator * (const Matrix& Ma_) const
	{
		Matrix res;
		for (int i = 0; i < m; ++i) {
			for (int j = 0; j < m; ++j) {
				for (int k = 0; k < m; ++k) {
					res.a[i][j] = (res.a[i][j] + a[i][k] * Ma_.a[k][j] % mod) % mod;
				}
			}
		}
		return res;
	}
};
Matrix quickpow(Matrix res,Matrix sta, ll b)//快速幂板子
{
	while (b > 0)
	{
		if (b & 1) res = res * sta;
		sta = sta * sta;
		b >>= 1;
	}
	return res;
}
void solve()
{
	m = (1 << M);
	Matrix state;
	Matrix trans;
	for (int i = 0; i < m; i++) {
			state.a[i][i] = 1;
	}
	for (int i = 0; i < m; i ++) {
		for (int j = 0; j < m; j ++) {
			if ((i | j)) {
				int flag = 0;
				for (int k = 0; k <= M; k ++) {
					if (((1 << k) & i) & ((1 << k) & j)) {
						flag = 1; 
						break;
					}
				}
				if (flag == 1)continue;
				trans.a[i][j] = 1;
			}
		}
	}//根据条件构造转移矩阵

	state = quickpow(state, trans, (n - 1));//快速幂

	int ans = 0;
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < m; j++) {
			ans =  (ans + state.a[i][j] + mod) % mod;
		}
	}
	cout << (ans + mod) % mod << endl;//方案数累加输出,注意取模
	return;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	while (cin >> M >> n) {
		solve();
	}
	return 0;
}
posted @ 2021-11-16 19:42  TomiokapEace  阅读(302)  评论(0)    收藏  举报