来自 ACM-ICPC SouthWestern Europe Regional Contest 2017
我正在尝试理解问题C(Macarons)。
皮埃尔以他的马卡龙闻名。他制作圆形马卡龙,存放在 尺寸为1×1的方形盒和椭圆形的食品,存放在 尺寸为1×2的矩形盒(或旋转后,尺寸为 尺寸2×1)。为了自助,皮埃尔希望 尺寸为N×M的矩形桌,上面有两种马卡龙, 表示表格必须完全填满,没有空白 剩下。桌子的宽度N小,客人可以 桌子的长度M很大,很容易抓住食品 容纳大量客人。为了保持桌子美观, 食品的方向应始终与食品的侧面对齐 表。皮埃尔(Pierre)希望知道有多少种方法可以平铺 表。 你能帮他吗?
输入
输入由以下整数组成:
•第一行中的N值(整数);
•第二行的M值(整数)。限制
输入满足1 <= N <= 8并且 1
输出
输出应包括在单行上以109为模的平铺总数。
样本输入
2
2
样本输出
7
样本输入
2
4
样本输出
71
问题是:有多少种方法可以完全覆盖2x1和1x1的MxN矩形? M和N分别为1到8和1到10 ^ 18。
我进行了一些研究,发现解决此类问题的有用工具是递归关系和矩阵(快速)求幂,以及使用位掩码。
但是,我不确定如何使用这些解决方案来实施解决方案。有什么想法吗?
编辑
这是一个官方解决方案。
#include <cstdio>
#include <vector>
#include <bits/stdc++.h>
using ::std::vector;
typedef vector<vector<long long>> mat;
const long long mod = 1000000000;
void printMat(const mat& a)
{
for (int i = 0; i < a.size(); i++)
{
for (int j = 0; j < a[0].size(); j++)
{
std::cout << a[i][j] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
}
mat mul(const mat& a, const mat& b) {
mat c = mat(a.size(), vector<long long>(b[0].size(), 0));
for (unsigned int i = 0; i < a.size(); ++i) {
for (unsigned int j = 0; j < b[0].size(); ++j) {
for (unsigned int k = 0; k < b.size(); ++k) {
c[i][j] += a[i][k] * b[k][j];
c[i][j] %= mod;
}
}
}
return c;
}
mat exp(const mat& a, long long n) {
if (n == 1ll) {
return a;
}
else {
mat b = exp(a, n / 2ll);
mat c = mul(b, b);
if (n % 2ll == 0) {
return c;
}
else {
return mul(c, a);
}
}
}
long long transitions(const mat& mem, int i, int j) {
if (j == 0) {
return 1;
}
if ((j & 1) == 0) {
return mem[i >> 1][j >> 1];
}
if ((j & 3) == 1) {
if ((i & 1) == 0) {
return mem[i >> 2][j >> 2];
}
else {
return 0;
}
}
if ((i & 1) == 0) {
return mem[i >> 2][j >> 2] + mem[i >> 1][j >> 1];
}
else {
return mem[i >> 2][j >> 2];
}
}
int main() {
int N;
long long M;
scanf("%d%lld", &N, &M);
if (N == 0 || M == 0) {
printf("0\n");
}
else {
int S = 1 << N;
mat T = mat(S, vector<long long>(S));
for (int i = 0; i < S; ++i) {
for (int j = 0; j < S; ++j) {
T[i][j] = transitions(T, i, j);
}
}
printMat(T);
mat TT = exp(T, M);
printMat(TT);
long long ans = 0;
for (int i = 0; i < S; ++i) {
ans += TT[S - 1][i];
}
printf("%lld\n", ans % 1000000000);
}
return 0;
}
我添加了函数printMat来检查基于输入形成的矩阵。我只是不了解矩阵的构造方式,以及为什么对最后一行的元素求和应该得出结果。