有多少种方法可以覆盖1x1和2x1的8xN矩形?

时间:2018-11-26 23:03:18

标签: c++ combinatorics tiling

来自 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来检查基于输入形成的矩阵。我只是不了解矩阵的构造方式,以及为什么对最后一行的元素求和应该得出结果。

0 个答案:

没有答案